From ad9ad6f402e3e15706519e59ef111a941d28d5af Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 17:42:57 +1200 Subject: [PATCH 0001/1110] Don't negate resulted offsets when `offset` is subtraction by 0 --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index cb44eccae68..f16b98883b8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -960,8 +960,8 @@ fn detect_manual_memcpy<'a, 'tcx>( 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), + ("0", _, x, false) | (x, false, "0", _) => x.into(), + ("0", _, x, true) => format!("-{}", x), (x, false, y, false) => format!("({} + {})", x, y), (x, false, y, true) => { if x == y { From 37261a904ce2fbd4137180500c57f75f29945828 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 17:51:01 +1200 Subject: [PATCH 0002/1110] Print 0 when `end` and `offset` is 0, and also simplify the suggestion --- clippy_lints/src/loops.rs | 15 ++++++++++++--- tests/ui/manual_memcpy.stderr | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f16b98883b8..6b5a8498dc9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -959,7 +959,7 @@ fn detect_manual_memcpy<'a, 'tcx>( 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", _, "0", _) => "0".into(), ("0", _, x, false) | (x, false, "0", _) => x.into(), ("0", _, x, true) => format!("-{}", x), (x, false, y, false) => format!("({} + {})", x, y), @@ -981,6 +981,15 @@ fn detect_manual_memcpy<'a, 'tcx>( } }; + let print_offset = |start_str: &Offset, inline_offset: &Offset| -> String { + let offset = print_sum(start_str, inline_offset); + if offset.as_str() == "0" { + "".into() + } else { + offset + } + }; + let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| { if let Some(end) = *end { if_chain! { @@ -1020,9 +1029,9 @@ fn detect_manual_memcpy<'a, 'tcx>( .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_offset = print_offset(&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_offset = print_offset(&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 diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 3dbb2155d4d..ec80f6070d6 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -58,13 +58,13 @@ 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 From 75ad839cd26c1da17fe6ba3aae1153ee96de26c8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 18:04:37 +1200 Subject: [PATCH 0003/1110] Do not trigger `manual_memcpy` for `RangeTo` --- clippy_lints/src/loops.rs | 52 ++++++++++++++++------------------- tests/ui/manual_memcpy.rs | 5 ++++ tests/ui/manual_memcpy.stderr | 2 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6b5a8498dc9..ca61c97e3e3 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -951,7 +951,7 @@ fn detect_manual_memcpy<'a, 'tcx>( ) { if let Some(higher::Range { start: Some(start), - ref end, + end: Some(end), limits, }) = higher::range(cx, arg) { @@ -990,35 +990,31 @@ fn detect_manual_memcpy<'a, 'tcx>( } }; - 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, ".len()"), offset.value) - } else { - String::new() - }; - } + let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { + 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, ".len()"), offset.value) + } else { + String::new() + }; } - - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&Offset::positive(end_str), &offset) - } else { - "..".into() } + + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + format!("{}", end + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), + }; + + print_sum(&Offset::positive(end_str), &offset) }; // The only statements in the for loops can be indexed assignments from diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index aa347288875..1f41838fa16 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -98,6 +98,11 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in from..from + 3 { dst[i] = src[i - from]; } + + // `RangeTo` `for` loop - don't trigger lint + for i in 0.. { + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index ec80f6070d6..95114c46f36 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -67,7 +67,7 @@ LL | for i in from..from + 3 { | ^^^^^^^^^^^^^^ 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:110:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` From c94f0f49f8e025aae11534f9f2b4c59c34b1edb8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 18:22:10 +1200 Subject: [PATCH 0004/1110] Remove all `ref` keyword --- clippy_lints/src/loops.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ca61c97e3e3..502bd42214e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -772,8 +772,8 @@ 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! @@ -821,8 +821,8 @@ fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool { fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option { fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { - 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,14 +831,14 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } - if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind { + if let ExprKind::Index(seqexpr, 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 { + ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { let offset_opt = if same_var(cx, lhs, var) { extract_offset(cx, rhs, var) @@ -878,7 +878,7 @@ fn fetch_cloned_fixed_offset_var<'a, 'tcx>( var: HirId, ) -> Option { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind; + 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); @@ -900,7 +900,7 @@ fn get_indexed_assignments<'a, 'tcx>( e: &Expr<'_>, var: HirId, ) -> Option<(FixedOffsetVar, FixedOffsetVar)> { - if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind { + if let ExprKind::Assign(lhs, rhs, _) = e.kind { match ( get_fixed_offset_var(cx, lhs, var), fetch_cloned_fixed_offset_var(cx, rhs, var), @@ -920,16 +920,14 @@ fn get_indexed_assignments<'a, 'tcx>( } } - if let ExprKind::Block(ref b, _) = body.kind { - let Block { - ref stmts, ref expr, .. - } = **b; + if let ExprKind::Block(b, _) = body.kind { + let Block { stmts, expr, .. } = *b; stmts .iter() .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(get_assignment(cx, e, var)), }) .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var)))) .filter_map(|op| op) @@ -992,7 +990,7 @@ fn detect_manual_memcpy<'a, 'tcx>( let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; + 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); From 7dd0f3459f558c1b557223a042f549b378cacae9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 18:47:24 +1200 Subject: [PATCH 0005/1110] Refactor `if` to use `else` and iterator combinators --- clippy_lints/src/loops.rs | 50 +++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 502bd42214e..3dd3a79b287 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -779,11 +779,11 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) - // our variable! if local_id == var; then { - return true; + true + } else { + false } } - - false } struct Offset { @@ -853,13 +853,7 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v 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())) - } else { - None - } - }, + ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), _ => None, }; @@ -883,11 +877,11 @@ fn fetch_cloned_fixed_offset_var<'a, 'tcx>( 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, arg, var) + } else { + get_fixed_offset_var(cx, expr, var) } } - - get_fixed_offset_var(cx, expr, var) } fn get_indexed_assignments<'a, 'tcx>( @@ -925,12 +919,12 @@ fn get_indexed_assignments<'a, 'tcx>( stmts .iter() - .map(|stmt| match stmt.kind { + .filter_map(|stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(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) + .chain(expr.into_iter()) + .map(|op| get_assignment(cx, op, var)) .collect::>>() .unwrap_or_default() } else { @@ -996,23 +990,23 @@ fn detect_manual_memcpy<'a, 'tcx>( if let Some(arg) = len_args.get(0); if snippet(cx, arg.span, "??") == var_name; then { - return if offset.negate { + if offset.negate { format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) } else { String::new() + } + } else { + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + format!("{}", end + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), }; + + print_sum(&Offset::positive(end_str), &offset) } } - - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&Offset::positive(end_str), &offset) }; // The only statements in the for loops can be indexed assignments from From 3f1e51b3f4a7bfb42c442caf2cb836ba62e2ba53 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 18:57:36 +1200 Subject: [PATCH 0006/1110] Rename `negate` to `sign` and make it strong types then make `art1` &str --- clippy_lints/src/loops.rs | 56 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3dd3a79b287..321d5265d0c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -786,20 +786,29 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) - } } +#[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, } } } @@ -949,31 +958,23 @@ fn detect_manual_memcpy<'a, 'tcx>( { // 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", _) => "0".into(), - ("0", _, x, false) | (x, false, "0", _) => x.into(), - ("0", _, x, true) => format!("-{}", x), - (x, false, y, false) => format!("({} + {})", x, y), - (x, false, y, true) => { + let 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) } }, - (x, true, y, false) => { - if x == y { - "0".into() - } else { - format!("({} - {})", y, x) - } - }, - (x, true, y, true) => format!("-({} + {})", x, y), } }; - let print_offset = |start_str: &Offset, inline_offset: &Offset| -> String { + let print_offset = |start_str: &str, inline_offset: &Offset| -> String { let offset = print_sum(start_str, inline_offset); if offset.as_str() == "0" { "".into() @@ -990,10 +991,9 @@ fn detect_manual_memcpy<'a, 'tcx>( if let Some(arg) = len_args.get(0); if snippet(cx, arg.span, "??") == var_name; then { - if offset.negate { - format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) - } else { - String::new() + match offset.sign { + OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), + OffsetSign::Positive => "".into(), } } else { let end_str = match limits { @@ -1004,7 +1004,7 @@ fn detect_manual_memcpy<'a, 'tcx>( ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), }; - print_sum(&Offset::positive(end_str), &offset) + print_sum(&end_str, &offset) } } }; @@ -1016,7 +1016,7 @@ fn detect_manual_memcpy<'a, 'tcx>( let big_sugg = manual_copies .into_iter() .map(|(dst_var, src_var)| { - let start_str = Offset::positive(snippet(cx, start.span, "").to_string()); + 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_name); let src_offset = print_offset(&start_str, &src_var.offset); From ecb472c052c746d87ce26f6b184f2c5f11537e0c Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:02:08 +1200 Subject: [PATCH 0007/1110] Use `fn` instead of closures where unnecessary --- clippy_lints/src/loops.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 321d5265d0c..e37c44dc026 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -958,7 +958,7 @@ fn detect_manual_memcpy<'a, 'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let print_sum = |arg1: &str, arg2: &Offset| -> 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(), @@ -972,16 +972,16 @@ fn detect_manual_memcpy<'a, 'tcx>( } }, } - }; + } - let print_offset = |start_str: &str, inline_offset: &Offset| -> String { + 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_name: &str| { if_chain! { From aab80eedf3e271ada92a6509727461cc3aa6bb12 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:04:56 +1200 Subject: [PATCH 0008/1110] Extract `get_fixed_offset_var`` from `fetch_cloned_fixed_offset_var` --- clippy_lints/src/loops.rs | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index e37c44dc026..2dc95f53078 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -828,6 +828,16 @@ 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 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_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option { fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { match &e.kind { @@ -875,24 +885,6 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } -fn fetch_cloned_fixed_offset_var<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, - expr: &Expr<'_>, - var: HirId, -) -> Option { - 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 { - get_fixed_offset_var(cx, arg, var) - } else { - get_fixed_offset_var(cx, expr, var) - } - } -} - fn get_indexed_assignments<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, body: &Expr<'_>, @@ -906,7 +898,7 @@ fn get_indexed_assignments<'a, 'tcx>( if let ExprKind::Assign(lhs, rhs, _) = e.kind { match ( get_fixed_offset_var(cx, lhs, var), - fetch_cloned_fixed_offset_var(cx, rhs, var), + get_fixed_offset_var(cx, fetch_cloned_expr(rhs), var), ) { (Some(offset_left), Some(offset_right)) => { // Source and destination must be different From 3d121d53af9a73ba11226715cd8132f6981ffee9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:15:51 +1200 Subject: [PATCH 0009/1110] Extract roles getting indexes from `get_indexed_assignments` --- clippy_lints/src/loops.rs | 106 ++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2dc95f53078..0753b23e45b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -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; @@ -885,52 +884,39 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } -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)> { +fn get_assignments<'a, 'tcx>( + body: &'tcx Expr<'tcx>, +) -> impl Iterator, &'tcx Expr<'tcx>)>> { + fn get_assignment<'a, 'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Assign(lhs, rhs, _) = e.kind { - match ( - get_fixed_offset_var(cx, lhs, var), - get_fixed_offset_var(cx, fetch_cloned_expr(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, - } + Some((lhs, rhs)) } else { None } } + // 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; + if let ExprKind::Block(b, _) = body.kind { let Block { stmts, expr, .. } = *b; - stmts + iter_a = stmts .iter() .filter_map(|stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain(expr.into_iter()) - .map(|op| get_assignment(cx, op, var)) - .collect::>>() - .unwrap_or_default() + .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()) } /// Checks for for loops that sequentially copy items from one slice-like @@ -1003,30 +989,48 @@ fn detect_manual_memcpy<'a, 'tcx>( // 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 Some(offset_left) = get_fixed_offset_var(cx, lhs, canonical_id); + if let Some(offset_right) = get_fixed_offset_var(cx, rhs, canonical_id); - let big_sugg = manual_copies - .into_iter() - .map(|(dst_var, src_var)| { - 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_name); - let src_offset = print_offset(&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 offset_left.var_name != offset_right.var_name; + then { + Some((offset_left, offset_right)) + } else { + return None + } + } + }) }) - .join("\n "); + .map(|o| { + o.map(|(dst_var, src_var)| { + 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_name); + let src_offset = print_offset(&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) + }; - if !big_sugg.is_empty() { + format!( + "{}.clone_from_slice(&{}[{}..{}])", + dst, src_var.var_name, src_offset, src_limit + ) + }) + }) + .collect::>>() + .filter(|v| !v.is_empty()) + .map(|v| v.join("\n ")); + + if let Some(big_sugg) = big_sugg { span_lint_and_sugg( cx, MANUAL_MEMCPY, From 4f2617c059f693ec72e5d31ad31fd85eba019ab1 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:26:00 +1200 Subject: [PATCH 0010/1110] Separate getting offsets and getting index expressions --- clippy_lints/src/loops.rs | 63 +++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0753b23e45b..75955997af2 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -837,7 +837,7 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option { +fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) -> Option { fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { @@ -849,38 +849,24 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } - if let ExprKind::Index(seqexpr, idx) = expr.kind { - let ty = cx.tables.expr_ty(seqexpr); - if !is_slice_like(cx, ty) { - return None; - } + 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 + }; - let offset = 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_opt.map(Offset::positive) }, - ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), + 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, } } @@ -994,15 +980,22 @@ fn detect_manual_memcpy<'a, 'tcx>( o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); if_chain! { - if let Some(offset_left) = get_fixed_offset_var(cx, lhs, canonical_id); - if let Some(offset_right) = get_fixed_offset_var(cx, rhs, canonical_id); + 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 var_name_left = snippet_opt(cx, seqexpr_left.span).unwrap_or_else(|| "???".into()); + let var_name_right = snippet_opt(cx, seqexpr_right.span).unwrap_or_else(|| "???".into()); // Source and destination must be different - if offset_left.var_name != offset_right.var_name; + if var_name_left != var_name_right; then { - Some((offset_left, offset_right)) + Some((FixedOffsetVar { var_name: var_name_left, offset: offset_left }, + FixedOffsetVar { var_name: var_name_right, offset: offset_right })) } else { - return None + None } } }) From 9fc6f37778789de94caa280f41afdf651bf5ae10 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:34:41 +1200 Subject: [PATCH 0011/1110] Delay getting the snippet from slices --- clippy_lints/src/loops.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 75955997af2..8ab35556670 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -812,8 +812,8 @@ impl Offset { } } -struct FixedOffsetVar { - var_name: String, +struct FixedOffsetVar<'hir> { + var: &'hir Expr<'hir>, offset: Offset, } @@ -947,13 +947,13 @@ fn detect_manual_memcpy<'a, 'tcx>( } } - let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { + 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 snippet(cx, arg.span, "??") == var_name; + if var_def_id(cx, arg) == var_def_id(cx, var); then { match offset.sign { OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), @@ -986,14 +986,12 @@ fn detect_manual_memcpy<'a, 'tcx>( && 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 var_name_left = snippet_opt(cx, seqexpr_left.span).unwrap_or_else(|| "???".into()); - let var_name_right = snippet_opt(cx, seqexpr_right.span).unwrap_or_else(|| "???".into()); // Source and destination must be different - if var_name_left != var_name_right; + if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right); then { - Some((FixedOffsetVar { var_name: var_name_left, offset: offset_left }, - FixedOffsetVar { var_name: var_name_right, offset: offset_right })) + Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left }, + FixedOffsetVar { var: seqexpr_right, offset: offset_right })) } else { None } @@ -1004,18 +1002,22 @@ fn detect_manual_memcpy<'a, 'tcx>( o.map(|(dst_var, src_var)| { 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_name); + 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_name); + 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.var_name + dst_var_name } else { - format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit) + format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var.var_name, src_offset, src_limit + dst, src_var_name, src_offset, src_limit ) }) }) From 582614fbbe76fed1b06feb640229b71a1886ffd7 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:44:44 +1200 Subject: [PATCH 0012/1110] Extract building the suggestion of `manual_memcpy` --- clippy_lints/src/loops.rs | 154 ++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8ab35556670..7cf3e16bef9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -905,6 +905,85 @@ fn get_assignments<'a, 'tcx>( 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, ".len()"), offset.value), + OffsetSign::Positive => "".into(), + } + } else { + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + 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>( @@ -922,57 +1001,6 @@ fn detect_manual_memcpy<'a, 'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - 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, ".len()"), offset.value), - OffsetSign::Positive => "".into(), - } - } else { - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&end_str, &offset) - } - } - }; - // The only statements in the for loops can be indexed assignments from // indexed retrievals. let big_sugg = get_assignments(body) @@ -998,29 +1026,7 @@ fn detect_manual_memcpy<'a, 'tcx>( } }) }) - .map(|o| { - o.map(|(dst_var, src_var)| { - 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 - ) - }) - }) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src))) .collect::>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); From 51585a129892f42eb23b0b37fea0e729f6678994 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 20:37:21 +1200 Subject: [PATCH 0013/1110] Removed unused lifetimes and a needless bool --- clippy_lints/src/loops.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7cf3e16bef9..5f7f0897943 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -775,10 +775,9 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) - 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 { - true + // our variable! + local_id == var } else { false } @@ -870,10 +869,8 @@ fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) } } -fn get_assignments<'a, 'tcx>( - body: &'tcx Expr<'tcx>, -) -> impl Iterator, &'tcx Expr<'tcx>)>> { - fn get_assignment<'a, 'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { +fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator, &'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 { From 1afb6e6e3b23bd5555f34cc4dcd20349dfd789de Mon Sep 17 00:00:00 2001 From: Stanislav Tkach Date: Tue, 28 Apr 2020 12:08:38 +0300 Subject: [PATCH 0014/1110] Extend example for the `unneeded_field_pattern` lint Current example is incorrect (or pseudo-code) because a struct name is omitted. I have used the code from the tests instead. Perhaps this example can be made less verbose, but I think it is more convenient to see a "real" code as an example. --- clippy_lints/src/misc_early.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index adfd8dfb1c1..62ee051624b 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -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, From 461f4a34660691675434a318ac4fd61a83444428 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 30 Apr 2020 17:32:37 +1200 Subject: [PATCH 0015/1110] Add missing tests --- tests/ui/manual_memcpy.rs | 10 ++++++++++ tests/ui/manual_memcpy.stderr | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 1f41838fa16..9c24d6d4db1 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -99,6 +99,16 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { 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]; diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 95114c46f36..bad84a58900 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -67,10 +67,22 @@ LL | for i in from..from + 3 { | ^^^^^^^^^^^^^^ 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:110: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 From f072ded3bf6286668ff8eade5b58e471dbe66f2a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 1 May 2020 01:44:17 +0200 Subject: [PATCH 0016/1110] Implement the manual_non_exhaustive lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_non_exhaustive.rs | 167 ++++++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/manual_non_exhaustive.rs | 124 ++++++++++++++++ tests/ui/manual_non_exhaustive.stderr | 48 +++++++ 6 files changed, 352 insertions(+) create mode 100644 clippy_lints/src/manual_non_exhaustive.rs create mode 100644 tests/ui/manual_non_exhaustive.rs create mode 100644 tests/ui/manual_non_exhaustive.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 575773580c0..facf363e371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1423,6 +1423,7 @@ Released 2018-09-13 [`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_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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c995be5edc2..64f3a8a0ebb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -247,6 +247,7 @@ mod literal_representation; mod loops; mod macro_use; mod main_recursion; +mod manual_non_exhaustive; mod map_clone; mod map_unit_fn; mod match_on_vec_items; @@ -628,6 +629,7 @@ 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_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, &map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN, @@ -1064,6 +1066,7 @@ 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_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1280,6 +1283,7 @@ 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_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), @@ -1474,6 +1478,7 @@ 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_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs new file mode 100644 index 00000000000..ca2a2cf2e1e --- /dev/null +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -0,0 +1,167 @@ +use crate::utils::{snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast::{Attribute, Ident, Item, ItemKind, StructField, TyKind, 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}; + +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! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)).count(); + if markers == 1 && variants.len() > markers; + if let Some(variant) = variants.last(); + if is_non_exhaustive_marker(variant); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + item.span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + diag.span_help(variant.span, "and 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(name: &Option) -> bool { + name.map(|n| n.as_str().starts_with('_')).unwrap_or(true) + } + + 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 !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + if private_fields == 1 && public_fields >= private_fields && public_fields == fields.len() - 1; + if let Some(field) = fields.iter().find(|f| is_private(f)); + if is_non_exhaustive_marker(&field.ident); + if let TyKind::Tup(tup_fields) = &field.ty.kind; + if tup_fields.is_empty(); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + let delimiter = match data { + VariantData::Struct(..) => '{', + VariantData::Tuple(..) => '(', + _ => unreachable!(), + }; + let header_span = cx.sess.source_map().span_until_char(item.span, delimiter); + + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + item.span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + diag.span_help(field.span, "and remove this field"); + } + }); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 72675c25175..c5360002fa0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1088,6 +1088,13 @@ pub static ref ALL_LINTS: Vec = 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", diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive.rs new file mode 100644 index 00000000000..9c239db6e00 --- /dev/null +++ b/tests/ui/manual_non_exhaustive.rs @@ -0,0 +1,124 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // last 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 not the last one, should be ignored + enum NotLast { + A, + #[doc(hidden)] + _B, + C, + } + + // variant with doc hidden is the only one, should be ignored + enum OnlyMarker { + #[doc(hidden)] + _A, + } + + // variant with multiple non-exhaustive "markers", should be ignored + enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, + } + + // already non_exhaustive, should be ignored + #[non_exhaustive] + enum NonExhaustive { + A, + B, + } +} + +mod structs { + struct S { + 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, should be ignored + #[non_exhaustive] + struct NonExhaustive { + pub a: i32, + pub b: i32, + } +} + +mod tuple_structs { + struct T(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, should be ignored + #[non_exhaustive] + struct NonExhaustive(pub i32, pub i32); +} + +fn main() {} diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive.stderr new file mode 100644 index 00000000000..d6719bca0d4 --- /dev/null +++ b/tests/ui/manual_non_exhaustive.stderr @@ -0,0 +1,48 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:5:5 + | +LL | / enum E { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ help: add the attribute: `#[non_exhaustive] enum E` + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: and 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:67:5 + | +LL | / struct S { +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ help: add the attribute: `#[non_exhaustive] struct S` + | +help: and remove this field + --> $DIR/manual_non_exhaustive.rs:70:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:108:5 + | +LL | struct T(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[non_exhaustive] struct T` + | +help: and remove this field + --> $DIR/manual_non_exhaustive.rs:108:32 + | +LL | struct T(pub i32, pub i32, ()); + | ^^ + +error: aborting due to 3 previous errors + From 42b0b4754c881101cefb0307c489d6159c19b2f3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 1 May 2020 22:37:14 +0200 Subject: [PATCH 0017/1110] Apply suggestions from PR review --- clippy_lints/src/manual_non_exhaustive.rs | 84 ++++++++++++----------- tests/ui/manual_non_exhaustive.rs | 39 +++++++---- tests/ui/manual_non_exhaustive.stderr | 81 ++++++++++++++++++---- 3 files changed, 139 insertions(+), 65 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index ca2a2cf2e1e..a4273da1d74 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,10 +1,11 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, Ident, Item, ItemKind, StructField, TyKind, Variant, VariantData, VisibilityKind}; +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. @@ -90,11 +91,9 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); - let markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)).count(); - if markers == 1 && variants.len() > markers; - if let Some(variant) = variants.last(); - if is_non_exhaustive_marker(variant); + 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, @@ -102,17 +101,20 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let header_span = cx.sess.source_map().span_until_char(item.span, '{'); - - if let Some(snippet) = snippet_opt(cx, header_span) { - diag.span_suggestion( - item.span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - diag.span_help(variant.span, "and remove this variant"); + 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"); }); } } @@ -123,8 +125,18 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: matches!(field.vis.node, VisibilityKind::Inherited) } - fn is_non_exhaustive_marker(name: &Option) -> bool { - name.map(|n| n.as_str().starts_with('_')).unwrap_or(true) + 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(..) => '(', + _ => unreachable!("`VariantData::Unit` is already handled above"), + }; + + cx.sess.source_map().span_until_char(item.span, delimiter) } let fields = data.fields(); @@ -132,12 +144,8 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); - if private_fields == 1 && public_fields >= private_fields && public_fields == fields.len() - 1; - if let Some(field) = fields.iter().find(|f| is_private(f)); - if is_non_exhaustive_marker(&field.ident); - if let TyKind::Tup(tup_fields) = &field.ty.kind; - if tup_fields.is_empty(); + 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, @@ -145,22 +153,20 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - _ => unreachable!(), - }; - let header_span = cx.sess.source_map().span_until_char(item.span, delimiter); - - if let Some(snippet) = snippet_opt(cx, header_span) { - diag.span_suggestion( - item.span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - diag.span_help(field.span, "and remove this field"); + 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"); }); } } diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive.rs index 9c239db6e00..7a788f48520 100644 --- a/tests/ui/manual_non_exhaustive.rs +++ b/tests/ui/manual_non_exhaustive.rs @@ -9,7 +9,16 @@ mod enums { _C, } - // last variant does not have doc hidden attribute, should be ignored + // 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, @@ -32,21 +41,13 @@ mod enums { _C(bool), } - // variant with doc hidden is not the last one, should be ignored - enum NotLast { - A, - #[doc(hidden)] - _B, - C, - } - // variant with doc hidden is the only one, should be ignored enum OnlyMarker { #[doc(hidden)] _A, } - // variant with multiple non-exhaustive "markers", should be ignored + // variant with multiple markers, should be ignored enum MultipleMarkers { A, #[doc(hidden)] @@ -55,7 +56,7 @@ mod enums { _C, } - // already non_exhaustive, should be ignored + // already non_exhaustive and no markers, should be ignored #[non_exhaustive] enum NonExhaustive { A, @@ -70,6 +71,14 @@ mod structs { _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, @@ -96,7 +105,7 @@ mod structs { _a: (), } - // already non exhaustive, should be ignored + // already non exhaustive and no private fields, should be ignored #[non_exhaustive] struct NonExhaustive { pub a: i32, @@ -107,6 +116,10 @@ mod structs { 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, ()); @@ -116,7 +129,7 @@ mod tuple_structs { // private field is the only field, should be ignored struct OnlyMarker(()); - // already non exhaustive, should be ignored + // already non exhaustive and no private fields, should be ignored #[non_exhaustive] struct NonExhaustive(pub i32, pub i32); } diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive.stderr index d6719bca0d4..613c5e8ca1d 100644 --- a/tests/ui/manual_non_exhaustive.stderr +++ b/tests/ui/manual_non_exhaustive.stderr @@ -1,48 +1,103 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> $DIR/manual_non_exhaustive.rs:5:5 | -LL | / enum E { +LL | enum E { + | ^----- + | | + | _____help: add the attribute: `#[non_exhaustive] enum E` + | | LL | | A, LL | | B, LL | | #[doc(hidden)] LL | | _C, LL | | } - | |_____^ help: add the attribute: `#[non_exhaustive] enum E` + | |_____^ | = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: and remove this variant +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:67:5 + --> $DIR/manual_non_exhaustive.rs:14:5 | -LL | / struct S { +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: add the attribute: `#[non_exhaustive] struct S` + | |_____^ | -help: and remove this field - --> $DIR/manual_non_exhaustive.rs:70:9 +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:108:5 + --> $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: add the attribute: `#[non_exhaustive] struct T` | -help: and remove this field - --> $DIR/manual_non_exhaustive.rs:108:32 +help: remove this field + --> $DIR/manual_non_exhaustive.rs:117:32 | LL | struct T(pub i32, pub i32, ()); | ^^ -error: aborting due to 3 previous errors +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 From 10e3f9bdb854e3cabbc4fda69ed713388344d524 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 1 May 2020 23:02:31 +0200 Subject: [PATCH 0018/1110] Move match_on_vec_items to pedantic --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/match_on_vec_items.rs | 2 +- src/lintlist/mod.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c995be5edc2..06e21a5272e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,6 +1138,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), @@ -1283,7 +1284,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1647,7 +1647,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), diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 4071406cc84..421571d2f2f 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// } /// ``` pub MATCH_ON_VEC_ITEMS, - correctness, + pedantic, "matching on vector elements can panic" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 72675c25175..f337db72ba0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1146,7 +1146,7 @@ pub static ref ALL_LINTS: Vec = 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", From 350c17de24c0bc7ee1b17981fe02f88ca6ec50a4 Mon Sep 17 00:00:00 2001 From: ebroto Date: Fri, 1 May 2020 23:00:16 +0200 Subject: [PATCH 0019/1110] Use the only variant left instead of a wildcard Co-authored-by: Philipp Krones --- clippy_lints/src/manual_non_exhaustive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index a4273da1d74..f3b8902e26f 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -133,7 +133,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: let delimiter = match data { VariantData::Struct(..) => '{', VariantData::Tuple(..) => '(', - _ => unreachable!("`VariantData::Unit` is already handled above"), + VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), }; cx.sess.source_map().span_until_char(item.span, delimiter) From 72ce6d5be9c54775b847bc0641f8d909b2977126 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 24 Apr 2020 16:46:56 +0200 Subject: [PATCH 0020/1110] Fix unwrap lint when checks are done in parameters --- clippy_lints/src/unwrap.rs | 21 ++++++++++----- .../ui/checked_unwrap/simple_conditionals.rs | 27 +++++++++++++++++++ .../checked_unwrap/simple_conditionals.stderr | 24 ++++++++--------- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 5235c98efab..f3844c7d3b6 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -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> { 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( diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index b0fc26ff76d..3e7b4b390ba 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -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 diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index e40542e2e4f..4013d1ed667 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -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 From d0c1f8ada2306801f2a6ce193e1f9f75471dbb3c Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 2 May 2020 14:18:27 +0300 Subject: [PATCH 0021/1110] fix fp on while-let-on-iterator - fix `is_refutable` for slice patterns - fix `is_refutable` for bindings - add some TODO-s for cases, which can not be fixed easily --- clippy_lints/src/utils/mod.rs | 34 ++++++++++------ tests/ui/while_let_on_iterator.fixed | 58 +++++++++++++++++++++++++++ tests/ui/while_let_on_iterator.rs | 58 +++++++++++++++++++++++++++ tests/ui/while_let_on_iterator.stderr | 28 ++++++++++--- 4 files changed, 160 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 04b4b423761..1c7b40fa908 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -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 + }, + } }, } } diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index f5fcabf63fd..e99c98ac79f 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -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(); diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 04dce8a0289..ba13172428e 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -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(); diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 6de138d7227..aa980d9965c 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -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 From e7138e06291d13163e8ab2a57fe2465451fac193 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 2 May 2020 14:25:45 +0300 Subject: [PATCH 0022/1110] Fix match on vec items: match on vec[..] - Added new tests - Fixed false positive when matching on full range, which will never panic --- clippy_lints/src/match_on_vec_items.rs | 21 ++++++++++++++++----- tests/ui/match_on_vec_items.rs | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 421571d2f2f..236362130e5 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{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}; @@ -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, &["core", "ops", "range", "RangeFull"]) +} diff --git a/tests/ui/match_on_vec_items.rs b/tests/ui/match_on_vec_items.rs index 0bb39d77e46..30415e3b94d 100644 --- a/tests/ui/match_on_vec_items.rs +++ b/tests/ui/match_on_vec_items.rs @@ -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(); } From de58c5644de0d9ffe46e7e2923d2301eaf4dd347 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 2 May 2020 17:36:26 +0300 Subject: [PATCH 0023/1110] Changed RANGE_FULL constant in utils --- clippy_lints/src/match_on_vec_items.rs | 4 ++-- clippy_lints/src/utils/paths.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 236362130e5..ee69628e9f0 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, match_type, 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}; @@ -96,5 +96,5 @@ fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { 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, &["core", "ops", "range", "RangeFull"]) + match_type(cx, ty, &utils::paths::RANGE_FULL) } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index ca2c4ade155..79ca6845c6f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -85,7 +85,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"]; From 17d877cce28b22b9b345ec7ef8b14859d8b165c4 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 3 May 2020 15:59:57 +0200 Subject: [PATCH 0024/1110] Update contributing section about syncing Clippy --- CONTRIBUTING.md | 62 +++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 50a5ee8bbf3..079a51eae3b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -155,47 +155,37 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun ## Fixing build failures caused by Rust -Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of -the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures -caused by Rust updates, can be a good way to learn about Rust internals. +Clippy currently gets build with `rustc` of the `rust-lang/rust` `master` +branch. Most of the times we have to adapt to the changes and only very rarely +there's an actual bug in Rust. -In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit -history][toolstate_commit_history]. You will then have to look for the last commit that contains -`test-pass -> build-fail` or `test-pass -> test-fail` for the `clippy-driver` component. -[Here][toolstate_commit] is an example. +If you decide to make Clippy work again with a Rust commit that breaks it, you +have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of +Clippy in the `rust-lang/rust` repository. -The commit message contains a link to the PR. The PRs are usually small enough to discover the breaking API change and -if they are bigger, they likely include some discussion that may help you to fix Clippy. +For general information about `subtree`s in the Rust repository see [Rust's +`CONTRIBUTING.md`][subtree]. -To check if Clippy is available for a specific target platform, you can check -the [rustup component history][rustup_component_history]. +Here is a TL;DR version of the sync process: -If you decide to make Clippy work again with a Rust commit that breaks it, -you probably want to install the latest Rust from master locally and run Clippy -using that version of Rust. +1. Clone the [`rust-lang/rust`] repository (all of the following commands have + to be run inside the `rust` directory) +2. Sync the changes to the copy of Clippy to your fork of the Clippy repository: + ```bash + # Make sure to change `your-github-name` to your github name in the following command + git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust + ``` +3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to + accelerate the process ping the `@rust-lang/clippy` team in your PR and/or + ~~annoy~~ ask them in the [Discord] channel.) +4. Sync the `rust-lang/rust-clippy` master to the copy of Clippy: + ```bash + git checkout -b sync-from-clippy + git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master + ``` +5. Open a PR to [`rust-lang/rust`] -You can set up the master toolchain by running `./setup-toolchain.sh`. That script will install -[rustup-toolchain-install-master][rtim] and master toolchain, then run `rustup override set master`. - -After fixing the build failure on this repository, we can submit a pull request -to [`rust-lang/rust`] to fix the toolstate. - -To submit a pull request, you should follow these steps: - -```bash -# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory -git submodule update --remote src/tools/clippy -cargo update -p clippy -git add -u -git commit -m "Update Clippy" -./x.py test -i --stage 1 src/tools/clippy # This is optional and should succeed anyway -# Open a PR in rust-lang/rust -``` - -[rustup_component_history]: https://rust-lang.github.io/rustup-components-history -[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master -[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/aad74d8294e198a7cf8ac81a91aebb7f3bbcf727 -[rtim]: https://github.com/kennytm/rustup-toolchain-install-master +[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust ## Issue and PR triage From c1698fedeb69109f9b1aebc0bfccd9bf3112ccad Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sun, 3 May 2020 16:47:57 +0200 Subject: [PATCH 0025/1110] Apply suggestions from code review Co-authored-by: Phil Hansch --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 079a51eae3b..f7a60938374 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -155,7 +155,7 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun ## Fixing build failures caused by Rust -Clippy currently gets build with `rustc` of the `rust-lang/rust` `master` +Clippy currently gets built with `rustc` of the `rust-lang/rust` `master` branch. Most of the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. @@ -170,7 +170,7 @@ Here is a TL;DR version of the sync process: 1. Clone the [`rust-lang/rust`] repository (all of the following commands have to be run inside the `rust` directory) -2. Sync the changes to the copy of Clippy to your fork of the Clippy repository: +2. Sync the changes to the rust-copy of Clippy to your Clippy fork: ```bash # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust @@ -178,7 +178,7 @@ Here is a TL;DR version of the sync process: 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Discord] channel.) -4. Sync the `rust-lang/rust-clippy` master to the copy of Clippy: +4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git checkout -b sync-from-clippy git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master From 7744cf4e53c4e19e5753e2063be420b9d0c0d38a Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 4 May 2020 15:13:07 +0200 Subject: [PATCH 0026/1110] Update to rustc changes --- clippy_lints/src/redundant_clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index d5cace0c647..d563eb886ae 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -591,7 +591,7 @@ struct PossibleBorrowerMap<'a, 'tcx> { impl PossibleBorrowerMap<'_, '_> { /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after(at); + self.maybe_live.seek_after_primary_effect(at); self.bitset.0.clear(); let maybe_live = &mut self.maybe_live; From 780a63a1ba84597eaf33e4d546bcf0edd6225836 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 5 May 2020 15:11:59 +0200 Subject: [PATCH 0027/1110] Update ui tests --- tests/ui/builtin-type-shadow.stderr | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr index b6a4adde848..bc785b075e0 100644 --- a/tests/ui/builtin-type-shadow.stderr +++ b/tests/ui/builtin-type-shadow.stderr @@ -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 From 3b58d66b22fe9107a78b99c267c71322276aa15a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 21:41:23 +0200 Subject: [PATCH 0028/1110] Add the manual_async_fn lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_async_fn.rs | 160 ++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 3 + src/lintlist/mod.rs | 7 ++ tests/ui/future_not_send.rs | 1 + tests/ui/future_not_send.stderr | 6 +- tests/ui/manual_async_fn.fixed | 55 ++++++++++ tests/ui/manual_async_fn.rs | 67 ++++++++++++ tests/ui/manual_async_fn.stderr | 93 ++++++++++++++++ 10 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/manual_async_fn.rs create mode 100644 tests/ui/manual_async_fn.fixed create mode 100644 tests/ui/manual_async_fn.rs create mode 100644 tests/ui/manual_async_fn.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index facf363e371..8457d6ad05c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1422,6 +1422,7 @@ 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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97785cba883..fb2e9932b8e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -247,6 +247,7 @@ 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; @@ -629,6 +630,7 @@ 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, @@ -1067,6 +1069,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1284,6 +1287,7 @@ 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), @@ -1478,6 +1482,7 @@ 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), diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs new file mode 100644 index 00000000000..ab8829abbf2 --- /dev/null +++ b/clippy_lints/src/manual_async_fn.rs @@ -0,0 +1,160 @@ +use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD}; +use crate::utils::{match_function_call, match_path, 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() -> Future { 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 async 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]; + let path = poly.trait_ref.path; + if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD); + 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))) + }, + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 79ca6845c6f..74040a96c78 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,6 +42,9 @@ 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_CORE: [&str; 3] = ["core", "future", "Future"]; +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; +pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"]; 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"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7685f67211..51d1cb2216a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1081,6 +1081,13 @@ pub static ref ALL_LINTS: Vec = 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", diff --git a/tests/ui/future_not_send.rs b/tests/ui/future_not_send.rs index 6d09d71a630..d3a920de4b6 100644 --- a/tests/ui/future_not_send.rs +++ b/tests/ui/future_not_send.rs @@ -41,6 +41,7 @@ impl Dummy { self.private_future().await; } + #[allow(clippy::manual_async_fn)] pub fn public_send(&self) -> impl std::future::Future { async { false } } diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index 3b4968ef0a6..d1863701bfe 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -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 | ^ 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) {} | ^ diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed new file mode 100644 index 00000000000..84c02bba4dc --- /dev/null +++ b/tests/ui/manual_async_fn.fixed @@ -0,0 +1,55 @@ +// 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 { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + async fn inh_fut() -> i32 { 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 { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs new file mode 100644 index 00000000000..bd5f9d1cf5f --- /dev/null +++ b/tests/ui/manual_async_fn.rs @@ -0,0 +1,67 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +fn fut() -> impl Future { + async { 42 } +} + +fn empty_fut() -> impl Future { + async {} +} + +fn core_fut() -> impl core::future::Future { + async move { 42 } +} + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + fn inh_fut() -> impl Future { + async { 42 } + } + + fn meth_fut(&self) -> impl Future { + async { 42 } + } + + fn empty_fut(&self) -> impl Future { + async {} + } + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr new file mode 100644 index 00000000000..016fdf95977 --- /dev/null +++ b/tests/ui/manual_async_fn.stderr @@ -0,0 +1,93 @@ +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:8:1 + | +LL | fn fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:12:1 + | +LL | fn empty_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +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 {} + | ^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:16:1 + | +LL | fn core_fut() -> impl core::future::Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +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 { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:38:5 + | +LL | fn inh_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +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 { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:42:5 + | +LL | fn meth_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +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 { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:46:5 + | +LL | fn empty_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +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 {} + | ^^ + +error: aborting due to 6 previous errors + From 4ac348b30849ec472564a81797b7e9ae42985460 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 22:03:38 +0200 Subject: [PATCH 0029/1110] Fix doc comment in lint declaration --- clippy_lints/src/manual_async_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index ab8829abbf2..b4be3ecfd16 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ```rust /// use std::future::Future; /// - /// fn foo() -> Future { async { 42 } } + /// fn foo() -> impl Future { async { 42 } } /// ``` /// Use instead: /// ```rust From 3e4bc026e2706b1cb21bad6d55726f8b5a1d4cf1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 22:40:28 +0200 Subject: [PATCH 0030/1110] Apply suggestions from PR review --- clippy_lints/src/manual_async_fn.rs | 9 ++++----- clippy_lints/src/utils/paths.rs | 2 -- tests/ui/manual_async_fn.fixed | 14 +++++++++++++- tests/ui/manual_async_fn.rs | 14 +++++++++++++- tests/ui/manual_async_fn.stderr | 25 +++++++++++++++---------- 5 files changed, 45 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index b4be3ecfd16..cb72a240582 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ -use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD}; -use crate::utils::{match_function_call, match_path, snippet_block, snippet_opt, span_lint_and_then}; +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; @@ -66,7 +66,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn { cx, MANUAL_ASYNC_FN, header_span, - "this function can be simplified using async syntax", + "this function can be simplified using the `async fn` syntax", |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); @@ -104,8 +104,7 @@ fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Opt if let ItemKind::OpaqueTy(opaque) = &item.kind; if opaque.bounds.len() == 1; if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - let path = poly.trait_ref.path; - if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD); + if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { return Some(&poly.trait_ref); } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 74040a96c78..b3ad2ad9d99 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,9 +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_CORE: [&str; 3] = ["core", "future", "Future"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"]; 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"]; diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 84c02bba4dc..6bb1032a172 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -29,7 +29,19 @@ async fn already_async() -> impl Future { struct S {} impl S { - async fn inh_fut() -> i32 { 42 } + 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 } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index bd5f9d1cf5f..d50c919188b 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -36,7 +36,19 @@ async fn already_async() -> impl Future { struct S {} impl S { fn inh_fut() -> impl Future { - async { 42 } + 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 { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index 016fdf95977..f278ee41aa3 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -1,4 +1,4 @@ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:8:1 | LL | fn fut() -> impl Future { @@ -14,7 +14,7 @@ help: move the body of the async block to the enclosing function LL | fn fut() -> impl Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:12:1 | LL | fn empty_fut() -> impl Future { @@ -29,7 +29,7 @@ help: move the body of the async block to the enclosing function LL | fn empty_fut() -> impl Future {} | ^^ -error: this function can be simplified using async syntax +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 { @@ -44,7 +44,7 @@ help: move the body of the async block to the enclosing function LL | fn core_fut() -> impl core::future::Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:38:5 | LL | fn inh_fut() -> impl Future { @@ -56,11 +56,16 @@ LL | async fn inh_fut() -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn inh_fut() -> impl Future { 42 } - | ^^^^^^ +LL | fn inh_fut() -> impl Future { +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 async syntax - --> $DIR/manual_async_fn.rs:42:5 +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 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,8 +79,8 @@ help: move the body of the async block to the enclosing function LL | fn meth_fut(&self) -> impl Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax - --> $DIR/manual_async_fn.rs:46:5 +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 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 438877380a2bf591fc1294ab31bc7fb2598738ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 9 May 2020 01:27:30 +0200 Subject: [PATCH 0031/1110] deps: remove unused regex dependency from root crate --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 63ce2cd8cad..6999b6bd740 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } From 0c14ea8ed79ebf0b7368659282136e876f247cc9 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sun, 3 May 2020 10:56:25 -0700 Subject: [PATCH 0032/1110] Allow 'use super::*;' imports --- clippy_lints/src/wildcard_imports.rs | 19 +++++++++++--- tests/ui/wildcard_imports.fixed | 38 ++++++++++++++++++++++++++++ tests/ui/wildcard_imports.rs | 38 ++++++++++++++++++++++++++++ tests/ui/wildcard_imports.stderr | 20 ++++++++++++++- 4 files changed, 111 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index f3038861cee..70ad9a60a02 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -3,7 +3,7 @@ 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}; @@ -83,8 +83,8 @@ impl LateLintPass<'_, '_> for WildcardImports { 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 !is_prelude_import(use_path.segments); + if !is_super_only_import_in_test(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 { @@ -154,3 +154,16 @@ impl LateLintPass<'_, '_> for WildcardImports { } } } + +// 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. +// This is intended primarily to ease the process of writing unit tests. +fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { + segments.iter().len() == 1 && + segments.first().map_or(false, |ps| ps.ident.as_str() == "super") +} diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ed6cc00ef04..003f11009a0 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -155,3 +155,41 @@ fn test_weird_formatting() { exported(); foo(); } + +mod test_super_imports { + fn foofoo() {} + + mod use_super { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit { + use test_super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit { + use super::super::test_super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index c6d6efaece8..7bd57c7965a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -156,3 +156,41 @@ fn test_weird_formatting() { exported(); foo(); } + +mod test_super_imports { + fn foofoo() {} + + mod use_super { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit { + use test_super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit { + use super::super::test_super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 050e4c6304f..858dc28797f 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -92,5 +92,23 @@ 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:172:13 + | +LL | use test_super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:181:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:190:13 + | +LL | use super::super::test_super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` + +error: aborting due to 18 previous errors From bdc75dbb7bbdc379b1f8cc346151fac4e63d7deb Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sun, 3 May 2020 11:18:10 -0700 Subject: [PATCH 0033/1110] Run `cargo dev fmt` --- clippy_lints/src/wildcard_imports.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 70ad9a60a02..e7400642b07 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -158,12 +158,14 @@ impl LateLintPass<'_, '_> for WildcardImports { // 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") + segments + .iter() + .last() + .map_or(false, |ps| ps.ident.as_str() == "prelude") } // Allow "super::*" imports. // This is intended primarily to ease the process of writing unit tests. fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { - segments.iter().len() == 1 && - segments.first().map_or(false, |ps| ps.ident.as_str() == "super") + segments.iter().len() == 1 && segments.first().map_or(false, |ps| ps.ident.as_str() == "super") } From 56f4e1c3a8be54c1c80de366618e83d8d7a6e9eb Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 08:06:30 -0700 Subject: [PATCH 0034/1110] Check if the parent module name contains "test" --- clippy_lints/src/wildcard_imports.rs | 19 ++++++++++++------- tests/ui/wildcard_imports.fixed | 16 ++++++++++++---- tests/ui/wildcard_imports.rs | 16 ++++++++++++---- tests/ui/wildcard_imports.stderr | 14 ++++++++++---- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7400642b07..a22d0e6775d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -84,7 +84,7 @@ impl LateLintPass<'_, '_> for WildcardImports { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if !is_prelude_import(use_path.segments); - if !is_super_only_import_in_test(use_path.segments); + if !(is_super_only_import(use_path.segments) && is_in_test_module(cx, item)); 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 +109,7 @@ impl LateLintPass<'_, '_> for WildcardImports { span = use_path.span.with_hi(item.span.hi() - BytePos(1)); } ( - span, - false, + span, false, ) }; @@ -164,8 +163,14 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { .map_or(false, |ps| ps.ident.as_str() == "prelude") } -// Allow "super::*" imports. -// This is intended primarily to ease the process of writing unit tests. -fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { - segments.iter().len() == 1 && segments.first().map_or(false, |ps| ps.ident.as_str() == "super") +// Allow "super::*" imports in tests. +fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { + segments.len() == 1 && segments[0].ident.as_str() == "super" +} + +fn is_in_test_module(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { + let parent = cx.tcx.hir().get_parent_node(item.hir_id); + let parent_item = cx.tcx.hir().expect_item(parent); + let parent_name = parent_item.ident.name.as_str(); + parent_name.contains("test") } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 003f11009a0..1c5c01f65d1 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -159,7 +159,15 @@ fn test_weird_formatting() { mod test_super_imports { fn foofoo() {} - mod use_super { + mod use_super_should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_super_in_test_should_pass { use super::*; fn with_super() { @@ -167,7 +175,7 @@ mod test_super_imports { } } - mod use_explicit { + mod use_explicit_should_be_replaced { use test_super_imports::foofoo; fn with_explicit() { @@ -175,7 +183,7 @@ mod test_super_imports { } } - mod use_double_super { + mod use_double_super_should_be_replaced { mod inner { use super::super::foofoo; @@ -185,7 +193,7 @@ mod test_super_imports { } } - mod use_super_explicit { + mod use_super_explicit_should_be_replaced { use super::super::test_super_imports::foofoo; fn with_super_explicit() { diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 7bd57c7965a..f783149ef93 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -160,7 +160,7 @@ fn test_weird_formatting() { mod test_super_imports { fn foofoo() {} - mod use_super { + mod use_super_should_be_replaced { use super::*; fn with_super() { @@ -168,7 +168,15 @@ mod test_super_imports { } } - mod use_explicit { + mod use_super_in_test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit_should_be_replaced { use test_super_imports::*; fn with_explicit() { @@ -176,7 +184,7 @@ mod test_super_imports { } } - mod use_double_super { + mod use_double_super_should_be_replaced { mod inner { use super::super::*; @@ -186,7 +194,7 @@ mod test_super_imports { } } - mod use_super_explicit { + mod use_super_explicit_should_be_replaced { use super::super::test_super_imports::*; fn with_super_explicit() { diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 858dc28797f..649d550a88d 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -93,22 +93,28 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:164:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:180:13 | LL | use test_super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:181:17 + --> $DIR/wildcard_imports.rs:189:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:190:13 + --> $DIR/wildcard_imports.rs:198:13 | LL | use super::super::test_super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors From ad92486d52fdede5c712dd27c868c942d8240704 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 14:41:54 -0700 Subject: [PATCH 0035/1110] Add check for "test" in parent name. Include flag for disabling wildcard import exceptions --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/utils/conf.rs | 2 + clippy_lints/src/wildcard_imports.rs | 48 +++++++++++++++---- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/wildcard_imports.fixed | 17 +++++-- tests/ui/wildcard_imports.rs | 17 +++++-- tests/ui/wildcard_imports.stderr | 14 +++--- 7 files changed, 75 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb2e9932b8e..4b67c84e38e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1058,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()); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4b81ff33495..57b9eafd14d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -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 { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index a22d0e6775d..43d0d1b9e96 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -6,7 +6,7 @@ use rustc_hir::{ 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! { @@ -73,18 +73,38 @@ declare_clippy_lint! { "lint `use _::*` statements" } -declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); +#[derive(Default)] +pub struct WildcardImports { + warn_on_all: bool, + is_test_module: bool, + test_modules_deep: u32, +} + +impl WildcardImports { + pub fn new(warn_on_all: bool) -> Self { + Self { + warn_on_all, + is_test_module: false, + 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 item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { return; } + if is_test_module(item) { + self.is_test_module = true; + self.test_modules_deep += 1; + } if_chain! { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if !is_prelude_import(use_path.segments); - if !(is_super_only_import(use_path.segments) && is_in_test_module(cx, item)); + if self.warn_on_all || !self.check_exceptions(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 { @@ -152,6 +172,19 @@ impl LateLintPass<'_, '_> for WildcardImports { } } } + + fn check_item_post(&mut self, _: &LateContext<'_, '_>, _: &Item<'_>) { + if self.is_test_module { + self.is_test_module = false; + self.test_modules_deep -= 1; + } + } +} + +impl WildcardImports { + fn check_exceptions(&self, segments: &[PathSegment<'_>]) -> bool { + is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + } } // Allow "...prelude::*" imports. @@ -168,9 +201,6 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.as_str() == "super" } -fn is_in_test_module(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { - let parent = cx.tcx.hir().get_parent_node(item.hir_id); - let parent_item = cx.tcx.hir().expect_item(parent); - let parent_name = parent_item.ident.name.as_str(); - parent_name.contains("test") +fn is_test_module(item: &Item<'_>) -> bool { + item.ident.name.as_str().contains("test") } diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 18f5d994ba8..53970af4107 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -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 diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 1c5c01f65d1..98bf6acfe55 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -156,10 +156,10 @@ fn test_weird_formatting() { foo(); } -mod test_super_imports { +mod super_imports { fn foofoo() {} - mod use_super_should_be_replaced { + mod should_be_replaced { use super::foofoo; fn with_super() { @@ -167,7 +167,7 @@ mod test_super_imports { } } - mod use_super_in_test_should_pass { + mod test_should_pass { use super::*; fn with_super() { @@ -175,8 +175,15 @@ mod test_super_imports { } } + mod inner { + fn test_should_pass() { + use super::*; + let _ = foofoo(); + } + } + mod use_explicit_should_be_replaced { - use test_super_imports::foofoo; + use super_imports::foofoo; fn with_explicit() { let _ = foofoo(); @@ -194,7 +201,7 @@ mod test_super_imports { } mod use_super_explicit_should_be_replaced { - use super::super::test_super_imports::foofoo; + use super::super::super_imports::foofoo; fn with_super_explicit() { let _ = foofoo(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index f783149ef93..9275c5a0928 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -157,10 +157,10 @@ fn test_weird_formatting() { foo(); } -mod test_super_imports { +mod super_imports { fn foofoo() {} - mod use_super_should_be_replaced { + mod should_be_replaced { use super::*; fn with_super() { @@ -168,7 +168,7 @@ mod test_super_imports { } } - mod use_super_in_test_should_pass { + mod test_should_pass { use super::*; fn with_super() { @@ -176,8 +176,15 @@ mod test_super_imports { } } + mod inner { + fn test_should_pass() { + use super::*; + let _ = foofoo(); + } + } + mod use_explicit_should_be_replaced { - use test_super_imports::*; + use super_imports::*; fn with_explicit() { let _ = foofoo(); @@ -195,7 +202,7 @@ mod test_super_imports { } mod use_super_explicit_should_be_replaced { - use super::super::test_super_imports::*; + use super::super::super_imports::*; fn with_super_explicit() { let _ = foofoo(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 649d550a88d..bd000ce8161 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -99,22 +99,22 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:180:13 + --> $DIR/wildcard_imports.rs:187:13 | -LL | use test_super_imports::*; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` +LL | use super_imports::*; + | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:189:17 + --> $DIR/wildcard_imports.rs:196:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:198:13 + --> $DIR/wildcard_imports.rs:205:13 | -LL | use super::super::test_super_imports::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: aborting due to 19 previous errors From a42a2bdac2a6c881f85ebdbce66e84d977c74cfa Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 14:48:27 -0700 Subject: [PATCH 0036/1110] Also have flag disable macro check --- clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 43d0d1b9e96..843ddda0356 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -102,7 +102,7 @@ impl LateLintPass<'_, '_> for WildcardImports { self.test_modules_deep += 1; } if_chain! { - if !in_macro(item.span); + if self.warn_on_all || !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if self.warn_on_all || !self.check_exceptions(use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); From 152cdcb45be7a8f0f24dbcd4177e0858d94516b6 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Fri, 8 May 2020 18:22:27 -0700 Subject: [PATCH 0037/1110] Remove unnecessary field, check for Mod/Fn ItemKind --- clippy_lints/src/wildcard_imports.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 843ddda0356..48405a00d55 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -76,7 +76,6 @@ declare_clippy_lint! { #[derive(Default)] pub struct WildcardImports { warn_on_all: bool, - is_test_module: bool, test_modules_deep: u32, } @@ -84,7 +83,6 @@ impl WildcardImports { pub fn new(warn_on_all: bool) -> Self { Self { warn_on_all, - is_test_module: false, test_modules_deep: 0, } } @@ -97,8 +95,7 @@ impl LateLintPass<'_, '_> for WildcardImports { if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { return; } - if is_test_module(item) { - self.is_test_module = true; + if is_test_module_or_function(item) { self.test_modules_deep += 1; } if_chain! { @@ -173,9 +170,8 @@ impl LateLintPass<'_, '_> for WildcardImports { } } - fn check_item_post(&mut self, _: &LateContext<'_, '_>, _: &Item<'_>) { - if self.is_test_module { - self.is_test_module = false; + fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) { + if is_test_module_or_function(item) { self.test_modules_deep -= 1; } } @@ -201,6 +197,6 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.as_str() == "super" } -fn is_test_module(item: &Item<'_>) -> bool { - item.ident.name.as_str().contains("test") +fn is_test_module_or_function(item: &Item<'_>) -> bool { + matches!(item.kind, ItemKind::Fn(..) | ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") } From 4db6abcd50eb07a7fa8349a059f80792b7b19a2e Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 08:04:07 -0700 Subject: [PATCH 0038/1110] Remove check for Fn, reflect this in test cases, make test cases more robust/explicit --- clippy_lints/src/wildcard_imports.rs | 13 +++++++++---- tests/ui/wildcard_imports.fixed | 25 +++++++++++++++++++++++-- tests/ui/wildcard_imports.rs | 24 ++++++++++++++++++++++-- tests/ui/wildcard_imports.stderr | 14 ++++++++++---- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 48405a00d55..e12a6659ab5 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -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. @@ -198,5 +203,5 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { } fn is_test_module_or_function(item: &Item<'_>) -> bool { - matches!(item.kind, ItemKind::Fn(..) | ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 98bf6acfe55..b47c8f23e24 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -175,13 +175,34 @@ mod super_imports { } } - mod inner { - fn test_should_pass() { + 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; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 9275c5a0928..3ad1a29aeba 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -176,13 +176,33 @@ mod super_imports { } } - mod inner { - fn test_should_pass() { + 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::*; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index bd000ce8161..de07bd1d69b 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -99,22 +99,28 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:187:13 + --> $DIR/wildcard_imports.rs:199:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:208:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:196:17 + --> $DIR/wildcard_imports.rs:217:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:205:13 + --> $DIR/wildcard_imports.rs:226:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors From a339766136a183327faaf13b987be30b2940872e Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 08:33:35 -0700 Subject: [PATCH 0039/1110] Fix test from auto-formatter fix --- tests/ui/wildcard_imports.fixed | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index b47c8f23e24..67423e6ec1d 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -202,7 +202,6 @@ mod super_imports { } } - mod use_explicit_should_be_replaced { use super_imports::foofoo; From 0ba61c612ee873314d252ca1f747c14a2f0161ba Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 10:14:29 -0700 Subject: [PATCH 0040/1110] Check is_macro inside check_exceptions, update references to fix test --- clippy_lints/src/wildcard_imports.rs | 13 +++++++------ tests/ui/wildcard_imports.stderr | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e12a6659ab5..2c4e24780e7 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -101,12 +101,11 @@ impl LateLintPass<'_, '_> for WildcardImports { return; } if is_test_module_or_function(item) { - self.test_modules_deep += 1; + self.test_modules_deep = self.test_modules_deep.saturating_add(1); } if_chain! { - if self.warn_on_all || !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if self.warn_on_all || !self.check_exceptions(use_path.segments); + 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 { @@ -177,14 +176,16 @@ impl LateLintPass<'_, '_> for WildcardImports { fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) { if is_test_module_or_function(item) { - self.test_modules_deep -= 1; + self.test_modules_deep = self.test_modules_deep.saturating_sub(1); } } } impl WildcardImports { - fn check_exceptions(&self, segments: &[PathSegment<'_>]) -> bool { - is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + 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) } } diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index de07bd1d69b..fab43b738eb 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -105,19 +105,19 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:208:13 + --> $DIR/wildcard_imports.rs:207:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:217:17 + --> $DIR/wildcard_imports.rs:216:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:226:13 + --> $DIR/wildcard_imports.rs:225:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` From b69200b8468434bc3f5b9ef8468733e5d40f4e01 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 10:16:47 -0700 Subject: [PATCH 0041/1110] Move is_test_module check to top of function --- clippy_lints/src/wildcard_imports.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2c4e24780e7..32d9a45c37d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -97,12 +97,12 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_, '_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { - if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { - return; - } 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 let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if self.warn_on_all || !self.check_exceptions(item, use_path.segments); From 318b8b6aabb2ef0fae0c0aafbba2c7ad97fb3a2a Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 10 May 2020 22:15:04 -0400 Subject: [PATCH 0042/1110] Add hint for collect type --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index f7a91fcdd21..8d75e126963 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1390,7 +1390,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_, '_>, did: DefId) -> bool .predicates .iter() .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }) - .collect(); + .collect::>(); !traits::normalize_and_test_predicates( cx.tcx, traits::elaborate_predicates(cx.tcx, predicates) From 01662d3a23fcd39f41fd15a8254fed814592e0c3 Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Thu, 7 May 2020 17:46:31 -0400 Subject: [PATCH 0043/1110] Fix nit and cargo.lock --- util/dev | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 util/dev diff --git a/util/dev b/util/dev new file mode 100755 index 00000000000..319de217e0d --- /dev/null +++ b/util/dev @@ -0,0 +1,7 @@ +#!/bin/sh +CARGO_TARGET_DIR=$(pwd)/target/ +export CARGO_TARGET_DIR + +echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.' + +cd clippy_dev && cargo run -- "$@" From 8ab3224b3b273f4911943800c56dc4aa925bc4c5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 May 2020 13:57:01 +0200 Subject: [PATCH 0044/1110] Fix clippy. --- clippy_lints/src/bytecount.rs | 7 ++++--- clippy_lints/src/inline_fn_without_body.rs | 5 +++-- clippy_lints/src/len_zero.rs | 6 +++--- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/map_clone.rs | 4 ++-- clippy_lints/src/non_expressive_names.rs | 4 ++-- clippy_lints/src/unsafe_removed_from_name.rs | 4 ++-- clippy_lints/src/utils/hir_utils.rs | 4 ++-- clippy_lints/src/utils/internal_lints.rs | 10 +++++----- clippy_lints/src/utils/mod.rs | 2 +- clippy_lints/src/utils/ptr.rs | 9 ++++----- clippy_lints/src/utils/usage.rs | 5 ++--- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 91d3e47d787..278d043732f 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -3,12 +3,13 @@ use crate::utils::{ span_lint_and_sugg, walk_ptrs_ty, }; use if_chain::if_chain; -use rustc_ast::ast::{Name, UintTy}; +use rustc_ast::ast::{UintTy}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Symbol; declare_clippy_lint! { /// **What it does:** Checks for naive byte counts @@ -95,11 +96,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount { } } -fn check_arg(name: Name, arg: Name, needle: &Expr<'_>) -> bool { +fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { name == arg && !contains_name(name, needle) } -fn get_path_name(expr: &Expr<'_>) -> Option { +fn get_path_name(expr: &Expr<'_>) -> Option { match expr.kind { ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => { get_path_name(e) diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 1ebfb3c8162..475610dda47 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -2,11 +2,12 @@ use crate::utils::span_lint_and_then; use crate::utils::sugg::DiagnosticBuilderExt; -use rustc_ast::ast::{Attribute, Name}; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Symbol; declare_clippy_lint! { /// **What it does:** Checks for `#[inline]` on trait methods without bodies @@ -38,7 +39,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InlineFnWithoutBody { } } -fn check_attrs(cx: &LateContext<'_, '_>, name: Name, attrs: &[Attribute]) { +fn check_attrs(cx: &LateContext<'_, '_>, name: Symbol, attrs: &[Attribute]) { for attr in attrs { if !attr.check_name(sym!(inline)) { continue; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 1d86ca9696f..2ec0b5a8d6f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,5 +1,5 @@ use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; -use rustc_ast::ast::{LitKind, Name}; +use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -7,7 +7,7 @@ use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, Ite use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::source_map::{Span, Spanned, Symbol}; declare_clippy_lint! { /// **What it does:** Checks for getting the length of something via `.len()` @@ -226,7 +226,7 @@ fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr fn check_len( cx: &LateContext<'_, '_>, span: Span, - method_name: Name, + method_name: Symbol, args: &[Expr<'_>], lit: &LitKind, op: &str, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4b67c84e38e..51b5401da7d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -335,7 +335,7 @@ mod zero_div_zero; pub use crate::utils::conf::Conf; mod reexport { - pub use rustc_ast::ast::Name; + pub use rustc_span::Symbol as Name; } /// Register all pre expansion lints diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 0b346393ac3..0163b3f8dbc 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -3,14 +3,14 @@ use crate::utils::{ is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg, }; use if_chain::if_chain; -use rustc_ast::ast::Ident; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 45809b35986..2b51b732075 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,13 +1,13 @@ use crate::utils::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ - Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Ident, Item, ItemKind, Local, MacCall, Pat, PatKind, + Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind, }; use rustc_ast::attr; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::{Ident, SymbolStr}; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 86c469a4dcc..735800e7e74 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -1,9 +1,9 @@ use crate::utils::span_lint; -use rustc_ast::ast::{Ident, Item, ItemKind, UseTree, UseTreeKind}; +use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::{Ident, SymbolStr}; declare_clippy_lint! { /// **What it does:** Checks for imports that remove "unsafe" from an item's diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 02b721fd378..bd7da57c665 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -1,6 +1,5 @@ use crate::consts::{constant_context, constant_simple}; use crate::utils::differing_macro_contexts; -use rustc_ast::ast::Name; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::{ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg, @@ -10,6 +9,7 @@ use rustc_hir::{ use rustc_lint::LateContext; use rustc_middle::ich::StableHashingContextProvider; use rustc_middle::ty::TypeckTables; +use rustc_span::Symbol; use std::hash::Hash; /// Type used to check whether two ast are the same. This is different from the @@ -544,7 +544,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } - pub fn hash_name(&mut self, n: Name) { + pub fn hash_name(&mut self, n: Symbol) { n.as_str().hash(&mut self.s); } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 5bf9acdc5f7..8e1b047f6f8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -4,7 +4,7 @@ use crate::utils::{ span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, }; use if_chain::if_chain; -use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, Name, NodeId}; +use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -17,7 +17,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::{Symbol, SymbolStr}; use std::borrow::{Borrow, Cow}; @@ -245,8 +245,8 @@ impl EarlyLintPass for ClippyLintsInternal { #[derive(Clone, Debug, Default)] pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, + declared_lints: FxHashMap, + registered_lints: FxHashSet, } impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]); @@ -357,7 +357,7 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool { } struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, + output: &'a mut FxHashSet, cx: &'a LateContext<'a, 'tcx>, } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 1c7b40fa908..3b8ef18bfab 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1077,7 +1077,7 @@ pub fn is_allowed(cx: &LateContext<'_, '_>, lint: &'static Lint, id: HirId) -> b cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow } -pub fn get_arg_name(pat: &Pat<'_>) -> Option { +pub fn get_arg_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ident, None) => Some(ident.name), PatKind::Ref(ref subpat, _) => get_arg_name(subpat), diff --git a/clippy_lints/src/utils/ptr.rs b/clippy_lints/src/utils/ptr.rs index 240bf2449cb..fb6bd5e8158 100644 --- a/clippy_lints/src/utils/ptr.rs +++ b/clippy_lints/src/utils/ptr.rs @@ -1,10 +1,9 @@ use crate::utils::{get_pat_name, match_var, snippet}; -use rustc_ast::ast::Name; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use rustc_span::source_map::Span; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; pub fn get_spans( @@ -25,7 +24,7 @@ pub fn get_spans( fn extract_clone_suggestions<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, - name: Name, + name: Symbol, replace: &[(&'static str, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { @@ -46,7 +45,7 @@ fn extract_clone_suggestions<'a, 'tcx>( struct PtrCloneVisitor<'a, 'tcx> { cx: &'a LateContext<'a, 'tcx>, - name: Name, + name: Symbol, replace: &'a [(&'static str, &'static str)], spans: Vec<(Span, Cow<'static, str>)>, abort: bool, @@ -83,6 +82,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { } } -fn get_binding_name(arg: &Param<'_>) -> Option { +fn get_binding_name(arg: &Param<'_>) -> Option { get_pat_name(&arg.pat) } diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index c14da6aacea..e8535677987 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,5 +1,4 @@ use crate::utils::match_var; -use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -8,7 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_middle::ty; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. @@ -78,7 +77,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { } pub struct UsedVisitor { - pub var: ast::Name, // var to look for + pub var: Symbol, // var to look for pub used: bool, // has the var been used otherwise? } From 33a3d852f53d38e738719c294c83bd03d50d2ac6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 11 May 2020 21:28:14 +0200 Subject: [PATCH 0045/1110] Fix fallout Re-remove util/dev file --- util/dev | 7 ------- 1 file changed, 7 deletions(-) delete mode 100755 util/dev diff --git a/util/dev b/util/dev deleted file mode 100755 index 319de217e0d..00000000000 --- a/util/dev +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -CARGO_TARGET_DIR=$(pwd)/target/ -export CARGO_TARGET_DIR - -echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.' - -cd clippy_dev && cargo run -- "$@" From 505280b108f777ae941d4056a87f77fb7f513a7e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 11 May 2020 21:31:01 +0200 Subject: [PATCH 0046/1110] Run cargo dev fmt --- clippy_lints/src/bytecount.rs | 2 +- clippy_lints/src/map_clone.rs | 2 +- clippy_lints/src/utils/usage.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 278d043732f..90c00ad098f 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -3,7 +3,7 @@ use crate::utils::{ span_lint_and_sugg, walk_ptrs_ty, }; use if_chain::if_chain; -use rustc_ast::ast::{UintTy}; +use rustc_ast::ast::UintTy; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 0163b3f8dbc..d5adf6b0f0d 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -9,8 +9,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index e8535677987..904d948ad29 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -77,8 +77,8 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { } pub struct UsedVisitor { - pub var: Symbol, // var to look for - pub used: bool, // has the var been used otherwise? + pub var: Symbol, // var to look for + pub used: bool, // has the var been used otherwise? } impl<'tcx> Visitor<'tcx> for UsedVisitor { From eec17d2c21605655188eaa13fd73d43c99162805 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 11 May 2020 21:40:33 +0200 Subject: [PATCH 0047/1110] Update failing test --- tests/ui/implicit_saturating_sub.fixed | 4 ++-- tests/ui/implicit_saturating_sub.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index e0b5b31a00c..859765d08a7 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -29,8 +29,8 @@ fn main() { // Lint u_16 = u_16.saturating_sub(1); - let mut end_32: u32 = 7000; - let mut start_32: u32 = 7010; + let mut end_32: u32 = 7010; + let mut start_32: u32 = 7000; let mut u_32: u32 = end_32 - start_32; diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 39d81608922..24cb216e79b 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -35,8 +35,8 @@ fn main() { u_16 -= 1; } - let mut end_32: u32 = 7000; - let mut start_32: u32 = 7010; + let mut end_32: u32 = 7010; + let mut start_32: u32 = 7000; let mut u_32: u32 = end_32 - start_32; From f20b96277397db2c9021d06cf8647014ccdc0a39 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 01:04:16 +0200 Subject: [PATCH 0048/1110] unused_unit: lint also in type parameters and where clauses --- clippy_lints/src/returns.rs | 64 ++++++++++++++++++++----------- tests/ui/unused_unit.fixed | 21 ++++++++-- tests/ui/unused_unit.rs | 18 +++++++-- tests/ui/unused_unit.stderr | 76 +++++++++++++++++++++++++++++++------ 4 files changed, 138 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 5c9117d5b81..35464f629c3 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -248,28 +248,7 @@ impl EarlyLintPass for Return { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { - let (rspan, appl) = if let Ok(fn_source) = - cx.sess().source_map() - .span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable) - } else { - (ty.span, Applicability::MaybeIncorrect) - } - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - rspan, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + lint_unneeded_unit_return(cx, ty, span); } } } @@ -313,6 +292,22 @@ impl EarlyLintPass for Return { _ => (), } } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } } fn attr_is_cfg(attr: &ast::Attribute) -> bool { @@ -337,3 +332,28 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { false } } + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + if let Some(rpos) = fn_source.rfind("->") { + #[allow(clippy::cast_possible_truncation)] + ( + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + } else { + (ty.span, Applicability::MaybeIncorrect) + } + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 3f63624720f..07f2791786d 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -14,11 +14,10 @@ struct Unitter; impl Unitter { - // try to disorient the lint with multiple unit returns and newlines #[allow(clippy::no_effect)] - pub fn get_unit (), G>(&self, f: F, _g: G) - where G: Fn() -> () { - let _y: &dyn Fn() -> () = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } @@ -30,6 +29,20 @@ impl Into<()> for Unitter { } } +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() ; +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() {} +} + fn return_unit() { } #[allow(clippy::needless_return)] diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 8fc072ebd69..e2c6afb020f 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -14,10 +14,8 @@ struct Unitter; impl Unitter { - // try to disorient the lint with multiple unit returns and newlines #[allow(clippy::no_effect)] - pub fn get_unit (), G>(&self, f: F, _g: G) -> - () + pub fn get_unit (), G>(&self, f: F, _g: G) -> () where G: Fn() -> () { let _y: &dyn Fn() -> () = &f; (); // this should not lint, as it's not in return type position @@ -31,6 +29,20 @@ impl Into<()> for Unitter { } } +trait Trait { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> (); +} + +impl Trait for Unitter { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> () {} +} + fn return_unit() -> () { () } #[allow(clippy::needless_return)] diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index a013d2b3495..81e6738e6bf 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,10 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:59 + --> $DIR/unused_unit.rs:18:29 | -LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> - | ___________________________________________________________^ -LL | | () - | |__________^ help: remove the `-> ()` +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -13,40 +11,94 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:29:19 + --> $DIR/unused_unit.rs:19:19 + | +LL | where G: Fn() -> () { + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:18:59 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:20:27 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:27:19 | LL | fn into(self) -> () { | ^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:30:9 + --> $DIR/unused_unit.rs:28:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:18 + --> $DIR/unused_unit.rs:33:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:35:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:36:17 + | +LL | H: Fn() -> (); + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:40:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:42:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:43:17 + | +LL | H: Fn() -> () {} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:46:18 | LL | fn return_unit() -> () { () } | ^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:34:26 + --> $DIR/unused_unit.rs:46:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:44:14 + --> $DIR/unused_unit.rs:56:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:46:11 + --> $DIR/unused_unit.rs:58:11 | LL | return(); | ^^ help: remove the `()` -error: aborting due to 7 previous errors +error: aborting due to 16 previous errors From 8ffa0bfaa2452eb9c80bf0f1909b039efc8dd0c3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 00:52:33 +0200 Subject: [PATCH 0049/1110] New lint: reversed_empty_ranges --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/ranges.rs | 112 +++++++++++++++++- tests/ui/reversed_empty_ranges_fixable.fixed | 24 ++++ tests/ui/reversed_empty_ranges_fixable.rs | 24 ++++ tests/ui/reversed_empty_ranges_fixable.stderr | 47 ++++++++ tests/ui/reversed_empty_ranges_unfixable.rs | 15 +++ .../ui/reversed_empty_ranges_unfixable.stderr | 34 ++++++ 8 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 tests/ui/reversed_empty_ranges_fixable.fixed create mode 100644 tests/ui/reversed_empty_ranges_fixable.rs create mode 100644 tests/ui/reversed_empty_ranges_fixable.stderr create mode 100644 tests/ui/reversed_empty_ranges_unfixable.rs create mode 100644 tests/ui/reversed_empty_ranges_unfixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 8457d6ad05c..33b277fbd31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1546,6 +1546,7 @@ Released 2018-09-13 [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51b5401da7d..e1cb10a4651 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -770,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_MINUS_ONE, &ranges::RANGE_PLUS_ONE, &ranges::RANGE_ZIP_WITH_LEN, + &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, @@ -1384,6 +1385,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&question_mark::QUESTION_MARK), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), @@ -1675,6 +1677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index d7ce2e66d69..86d55ccabb6 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,14 +1,17 @@ +use crate::consts::{constant, Constant}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use std::cmp::Ordering; use crate::utils::sugg::Sugg; +use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; use crate::utils::{higher, SpanlessEq}; -use crate::utils::{is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for zipping a collection with the range of @@ -84,10 +87,44 @@ declare_clippy_lint! { "`x..=(y-1)` reads better as `x..y`" } +declare_clippy_lint! { + /// **What it does:** Checks for range expressions `x..y` where both `x` and `y` + /// are constant and `x` is greater or equal to `y`. + /// + /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op. + /// Moreover, trying to use a reversed range to index a slice will panic at run-time. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// (10..=0).for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[3..1]; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// (0..=10).rev().for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[1..3]; + /// } + /// ``` + pub REVERSED_EMPTY_RANGES, + correctness, + "reversing the limits of range expressions, resulting in empty ranges" +} + declare_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, - RANGE_MINUS_ONE + RANGE_MINUS_ONE, + REVERSED_EMPTY_RANGES, ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { @@ -124,6 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { check_exclusive_range_plus_one(cx, expr); check_inclusive_range_minus_one(cx, expr); + check_reversed_empty_range(cx, expr); } } @@ -202,6 +240,76 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } } +fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::Index(..), + .. + }) + ) + } + + fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { + match limits { + RangeLimits::HalfOpen => ordering != Ordering::Less, + RangeLimits::Closed => ordering == Ordering::Greater, + } + } + + if_chain! { + if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr); + let ty = cx.tables.expr_ty(start); + if let ty::Int(_) | ty::Uint(_) = ty.kind; + if let Some((start_idx, _)) = constant(cx, cx.tables, start); + if let Some((end_idx, _)) = constant(cx, cx.tables, end); + if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); + if is_empty_range(limits, ordering); + then { + if inside_indexing_expr(cx, expr) { + let (reason, outcome) = if ordering == Ordering::Equal { + ("empty", "always yield an empty slice") + } else { + ("reversed", "panic at run-time") + }; + + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + &format!("this range is {} and using it to index a slice will {}", reason, outcome), + ); + } else { + span_lint_and_then( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is empty so it will yield no values", + |diag| { + if ordering != Ordering::Equal { + let start_snippet = snippet(cx, start.span, "_"); + let end_snippet = snippet(cx, end.span, "_"); + let dots = match limits { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=" + }; + + diag.span_suggestion( + expr.span, + "consider using the following if you are attempting to iterate over this \ + range in reverse", + format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + Applicability::MaybeIncorrect, + ); + } + }, + ); + } + } + } +} + fn y_plus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { match expr.kind { ExprKind::Binary( diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed new file mode 100644 index 00000000000..ee2cbc3cf54 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (21..=42).rev().for_each(|x| println!("{}", x)); + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in (-42..=-21).rev() {} + for _ in (21u32..42u32).rev() {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs new file mode 100644 index 00000000000..6ed5ca6daa0 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (42..=21).for_each(|x| println!("{}", x)); + let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in -21..=-42 {} + for _ in 42u32..21u32 {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr new file mode 100644 index 00000000000..97933b8ff85 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -0,0 +1,47 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + | +LL | (42..=21).for_each(|x| println!("{}", x)); + | ^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | (21..=42).rev().for_each(|x| println!("{}", x)); + | ^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + | +LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:10:14 + | +LL | for _ in -21..=-42 {} + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (-42..=-21).rev() {} + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + | +LL | for _ in 42u32..21u32 {} + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (21u32..42u32).rev() {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs new file mode 100644 index 00000000000..c9ca4c47668 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -0,0 +1,15 @@ +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; +const SOME_NUM: usize = 3; + +fn main() { + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[3usize..=1usize]; + let _ = &arr[SOME_NUM..1]; + let _ = &arr[3..3]; + + for _ in ANSWER..ANSWER {} +} diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr new file mode 100644 index 00000000000..12e5483ecdf --- /dev/null +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -0,0 +1,34 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 + | +LL | let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:10:18 + | +LL | let _ = &arr[3usize..=1usize]; + | ^^^^^^^^^^^^^^^ + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:11:18 + | +LL | let _ = &arr[SOME_NUM..1]; + | ^^^^^^^^^^^ + +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 + | +LL | let _ = &arr[3..3]; + | ^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + | +LL | for _ in ANSWER..ANSWER {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + From 0f2b1193f9501ffd06f9bf2ea8ab85a4db92f47b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 00:53:31 +0200 Subject: [PATCH 0050/1110] Remove reverse_range_loop lint --- CHANGELOG.md | 1 - clippy_lints/src/lib.rs | 7 +- clippy_lints/src/loops.rs | 102 +---------------------------- src/lintlist/mod.rs | 6 +- tests/ui/for_loop_fixable.fixed | 54 --------------- tests/ui/for_loop_fixable.rs | 54 --------------- tests/ui/for_loop_fixable.stderr | 88 +++++-------------------- tests/ui/for_loop_unfixable.rs | 18 ----- tests/ui/for_loop_unfixable.stderr | 11 ++-- tests/ui/manual_memcpy.rs | 2 +- 10 files changed, 32 insertions(+), 311 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33b277fbd31..b25ef049356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1545,7 +1545,6 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used -[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e1cb10a4651..0c4daeb731f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -624,7 +624,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_COLLECT, &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, - &loops::REVERSE_RANGE_LOOP, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, @@ -1284,7 +1283,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_COLLECT), LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1658,7 +1656,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::FOR_LOOP_OVER_RESULT), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), @@ -1788,6 +1785,10 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { "unsafe_vector_initialization", "the replacement suggested by this lint had substantially different behavior", ); + store.register_removed( + "reverse_range_loop", + "this lint is now included in reversed_empty_ranges", + ); } /// Register renamed lints. diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2bbf4dba614..0bc6b70855b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use crate::consts::constant; use crate::reexport::Name; use crate::utils::paths; use crate::utils::usage::{is_unused, mutated_variables}; @@ -8,7 +8,7 @@ use crate::utils::{ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg}; +use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -270,30 +270,6 @@ declare_clippy_lint! { "collecting an iterator when collect is not needed" } -declare_clippy_lint! { - /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y` - /// are constant and `x` is greater or equal to `y`, unless the range is - /// reversed or has a negative `.step_by(_)`. - /// - /// **Why is it bad?** Such loops will either be skipped or loop until - /// wrap-around (in debug code, this may `panic!()`). Both options are probably - /// not intended. - /// - /// **Known problems:** The lint cannot catch loops over dynamically defined - /// ranges. Doing this would require simulating all possible inputs and code - /// paths through the program, which would be complex and error-prone. - /// - /// **Example:** - /// ```ignore - /// for x in 5..10 - 5 { - /// .. - /// } // oops, stray `-` - /// ``` - pub REVERSE_RANGE_LOOP, - correctness, - "iteration over an empty range, such as `10..0` or `5..5`" -} - declare_clippy_lint! { /// **What it does:** Checks `for` loops over slices with an explicit counter /// and suggests the use of `.enumerate()`. @@ -463,7 +439,6 @@ declare_lint_pass!(Loops => [ FOR_LOOP_OVER_OPTION, WHILE_LET_LOOP, NEEDLESS_COLLECT, - REVERSE_RANGE_LOOP, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -761,7 +736,6 @@ fn check_for_loop<'a, 'tcx>( expr: &'tcx Expr<'_>, ) { check_for_loop_range(cx, pat, arg, body, expr); - check_for_loop_reverse_range(cx, arg, expr); check_for_loop_arg(cx, pat, arg, expr); check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); @@ -1248,78 +1222,6 @@ fn is_end_eq_array_len<'tcx>( false } -fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - // if this for loop is iterating over a two-sided range... - if let Some(higher::Range { - start: Some(start), - end: Some(end), - limits, - }) = higher::range(cx, arg) - { - // ...and both sides are compile-time constant integers... - if let Some((start_idx, _)) = constant(cx, cx.tables, start) { - if let Some((end_idx, _)) = constant(cx, cx.tables, end) { - // ...and the start index is greater than the end index, - // this loop will never run. This is often confusing for developers - // who think that this will iterate from the larger value to the - // smaller value. - let ty = cx.tables.expr_ty(start); - let (sup, eq) = match (start_idx, end_idx) { - (Constant::Int(start_idx), Constant::Int(end_idx)) => ( - match ty.kind { - ty::Int(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity), - ty::Uint(_) => start_idx > end_idx, - _ => false, - }, - start_idx == end_idx, - ), - _ => (false, false), - }; - - if sup { - let start_snippet = snippet(cx, start.span, "_"); - let end_snippet = snippet(cx, end.span, "_"); - let dots = if limits == ast::RangeLimits::Closed { - "..=" - } else { - ".." - }; - - span_lint_and_then( - cx, - REVERSE_RANGE_LOOP, - expr.span, - "this range is empty so this for loop will never run", - |diag| { - diag.span_suggestion( - arg.span, - "consider using the following if you are attempting to iterate over this \ - range in reverse", - format!( - "({end}{dots}{start}).rev()", - end = end_snippet, - dots = dots, - start = start_snippet - ), - Applicability::MaybeIncorrect, - ); - }, - ); - } else if eq && limits != ast::RangeLimits::Closed { - // if they are equal, it's also problematic - this loop - // will never run. - span_lint( - cx, - REVERSE_RANGE_LOOP, - expr.span, - "this range is empty so this for loop will never run", - ); - } - } - } - } -} - fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { let mut applicability = Applicability::MachineApplicable; let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 51d1cb2216a..e1a6d4bdd31 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1922,11 +1922,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "reverse_range_loop", + name: "reversed_empty_ranges", group: "correctness", - desc: "iteration over an empty range, such as `10..0` or `5..5`", + desc: "reversing the limits of range expressions, resulting in empty ranges", deprecation: None, - module: "loops", + module: "ranges", }, Lint { name: "same_functions_in_if_condition", diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index 5fc84ada9ef..249a88a0b39 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -21,7 +21,6 @@ impl Unrelated { clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -32,61 +31,8 @@ impl Unrelated { )] #[allow(clippy::many_single_char_names, unused_variables)] fn main() { - const MAX_LEN: usize = 42; let mut vec = vec![1, 2, 3, 4]; - for i in (0..10).rev() { - println!("{}", i); - } - - for i in (0..=10).rev() { - println!("{}", i); - } - - for i in (0..MAX_LEN).rev() { - println!("{}", i); - } - - for i in 5..=5 { - // not an error, this is the range with only one element “5” - println!("{}", i); - } - - for i in 0..10 { - // not an error, the start index is less than the end index - println!("{}", i); - } - - for i in -10..0 { - // not an error - println!("{}", i); - } - - for i in (10..0).map(|x| x * 2) { - // not an error, it can't be known what arbitrary methods do to a range - println!("{}", i); - } - - // testing that the empty range lint folds constants - for i in (5 + 4..10).rev() { - println!("{}", i); - } - - for i in ((3 - 1)..(5 + 2)).rev() { - println!("{}", i); - } - - for i in (2 * 2)..(2 * 3) { - // no error, 4..6 is fine - println!("{}", i); - } - - let x = 42; - for i in x..10 { - // no error, not constant-foldable - println!("{}", i); - } - // See #601 for i in 0..10 { // no error, id_col does not exist outside the loop diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 4165b0dc004..306d85a6351 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -21,7 +21,6 @@ impl Unrelated { clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -32,61 +31,8 @@ impl Unrelated { )] #[allow(clippy::many_single_char_names, unused_variables)] fn main() { - const MAX_LEN: usize = 42; let mut vec = vec![1, 2, 3, 4]; - for i in 10..0 { - println!("{}", i); - } - - for i in 10..=0 { - println!("{}", i); - } - - for i in MAX_LEN..0 { - println!("{}", i); - } - - for i in 5..=5 { - // not an error, this is the range with only one element “5” - println!("{}", i); - } - - for i in 0..10 { - // not an error, the start index is less than the end index - println!("{}", i); - } - - for i in -10..0 { - // not an error - println!("{}", i); - } - - for i in (10..0).map(|x| x * 2) { - // not an error, it can't be known what arbitrary methods do to a range - println!("{}", i); - } - - // testing that the empty range lint folds constants - for i in 10..5 + 4 { - println!("{}", i); - } - - for i in (5 + 2)..(3 - 1) { - println!("{}", i); - } - - for i in (2 * 2)..(2 * 3) { - // no error, 4..6 is fine - println!("{}", i); - } - - let x = 42; - for i in x..10 { - // no error, not constant-foldable - println!("{}", i); - } - // See #601 for i in 0..10 { // no error, id_col does not exist outside the loop diff --git a/tests/ui/for_loop_fixable.stderr b/tests/ui/for_loop_fixable.stderr index cffb4b9f0a9..ddfe66d675f 100644 --- a/tests/ui/for_loop_fixable.stderr +++ b/tests/ui/for_loop_fixable.stderr @@ -1,61 +1,5 @@ -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:38:14 - | -LL | for i in 10..0 { - | ^^^^^ - | - = note: `-D clippy::reverse-range-loop` implied by `-D warnings` -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..10).rev() { - | ^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:42:14 - | -LL | for i in 10..=0 { - | ^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..=10).rev() { - | ^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:46:14 - | -LL | for i in MAX_LEN..0 { - | ^^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..MAX_LEN).rev() { - | ^^^^^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:71:14 - | -LL | for i in 10..5 + 4 { - | ^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (5 + 4..10).rev() { - | ^^^^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:75:14 - | -LL | for i in (5 + 2)..(3 - 1) { - | ^^^^^^^^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in ((3 - 1)..(5 + 2)).rev() { - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:97:15 + --> $DIR/for_loop_fixable.rs:43:15 | LL | for _v in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -63,13 +7,13 @@ LL | for _v in vec.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:99:15 + --> $DIR/for_loop_fixable.rs:45:15 | LL | for _v in vec.iter_mut() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:102:15 + --> $DIR/for_loop_fixable.rs:48:15 | LL | for _v in out_vec.into_iter() {} | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` @@ -77,76 +21,76 @@ LL | for _v in out_vec.into_iter() {} = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:107:15 + --> $DIR/for_loop_fixable.rs:53:15 | LL | for _v in [1, 2, 3].iter() {} | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:111:15 + --> $DIR/for_loop_fixable.rs:57:15 | LL | for _v in [0; 32].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:116:15 + --> $DIR/for_loop_fixable.rs:62:15 | LL | for _v in ll.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&ll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:119:15 + --> $DIR/for_loop_fixable.rs:65:15 | LL | for _v in vd.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&vd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:122:15 + --> $DIR/for_loop_fixable.rs:68:15 | LL | for _v in bh.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bh` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:125:15 + --> $DIR/for_loop_fixable.rs:71:15 | LL | for _v in hm.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hm` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:128:15 + --> $DIR/for_loop_fixable.rs:74:15 | LL | for _v in bt.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bt` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:131:15 + --> $DIR/for_loop_fixable.rs:77:15 | LL | for _v in hs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:134:15 + --> $DIR/for_loop_fixable.rs:80:15 | LL | for _v in bs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bs` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:309:18 + --> $DIR/for_loop_fixable.rs:255:18 | LL | for i in iterator.into_iter() { | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:329:18 + --> $DIR/for_loop_fixable.rs:275:18 | LL | for _ in t.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:331:18 + --> $DIR/for_loop_fixable.rs:277:18 | LL | for _ in r.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 20 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index 179b255e08c..e73536052f0 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -5,7 +5,6 @@ clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -16,25 +15,8 @@ unused, dead_code )] -#[allow(clippy::many_single_char_names, unused_variables)] fn main() { - for i in 5..5 { - println!("{}", i); - } - let vec = vec![1, 2, 3, 4]; for _v in vec.iter().next() {} - - for i in (5 + 2)..(8 - 1) { - println!("{}", i); - } - - const ZERO: usize = 0; - - for i in ZERO..vec.len() { - if f(&vec[i], &vec[i]) { - panic!("at the disco"); - } - } } diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index 1da8e0f3588..1c9287b6acb 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,9 +1,10 @@ -error[E0425]: cannot find function `f` in this scope - --> $DIR/for_loop_unfixable.rs:36:12 +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loop_unfixable.rs:21:15 | -LL | if f(&vec[i], &vec[i]) { - | ^ help: a local variable with a similar name exists: `i` +LL | for _v in vec.iter().next() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-next-loop` implied by `-D warnings` error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 9c24d6d4db1..0083f94798f 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -104,7 +104,7 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { dst[i - 0] = src[i]; } - #[allow(clippy::reverse_range_loop)] + #[allow(clippy::reversed_empty_ranges)] for i in 0..0 { dst[i] = src[i]; } From 064431d22fbc28f958a1d18da8a5e01ff99dadb0 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 23:48:48 +0200 Subject: [PATCH 0051/1110] Re-add old tests for empty range loops --- .../reversed_empty_ranges_loops_fixable.fixed | 57 +++++++++++++++ .../ui/reversed_empty_ranges_loops_fixable.rs | 57 +++++++++++++++ ...reversed_empty_ranges_loops_fixable.stderr | 69 +++++++++++++++++++ .../reversed_empty_ranges_loops_unfixable.rs | 11 +++ ...versed_empty_ranges_loops_unfixable.stderr | 16 +++++ 5 files changed, 210 insertions(+) create mode 100644 tests/ui/reversed_empty_ranges_loops_fixable.fixed create mode 100644 tests/ui/reversed_empty_ranges_loops_fixable.rs create mode 100644 tests/ui/reversed_empty_ranges_loops_fixable.stderr create mode 100644 tests/ui/reversed_empty_ranges_loops_unfixable.rs create mode 100644 tests/ui/reversed_empty_ranges_loops_unfixable.stderr diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/tests/ui/reversed_empty_ranges_loops_fixable.fixed new file mode 100644 index 00000000000..f1503ed6d12 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + const MAX_LEN: usize = 42; + + for i in (0..10).rev() { + println!("{}", i); + } + + for i in (0..=10).rev() { + println!("{}", i); + } + + for i in (0..MAX_LEN).rev() { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (0..10).rev().map(|x| x * 2) { + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in (5 + 4..10).rev() { + println!("{}", i); + } + + for i in ((3 - 1)..(5 + 2)).rev() { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } +} diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.rs b/tests/ui/reversed_empty_ranges_loops_fixable.rs new file mode 100644 index 00000000000..a733788dc22 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_fixable.rs @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + const MAX_LEN: usize = 42; + + for i in 10..0 { + println!("{}", i); + } + + for i in 10..=0 { + println!("{}", i); + } + + for i in MAX_LEN..0 { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (10..0).map(|x| x * 2) { + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in 10..5 + 4 { + println!("{}", i); + } + + for i in (5 + 2)..(3 - 1) { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } +} diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/tests/ui/reversed_empty_ranges_loops_fixable.stderr new file mode 100644 index 00000000000..e89e040a0ff --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -0,0 +1,69 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14 + | +LL | for i in 10..0 { + | ^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev() { + | ^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14 + | +LL | for i in 10..=0 { + | ^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..=10).rev() { + | ^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14 + | +LL | for i in MAX_LEN..0 { + | ^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..MAX_LEN).rev() { + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14 + | +LL | for i in (10..0).map(|x| x * 2) { + | ^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev().map(|x| x * 2) { + | ^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14 + | +LL | for i in 10..5 + 4 { + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (5 + 4..10).rev() { + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14 + | +LL | for i in (5 + 2)..(3 - 1) { + | ^^^^^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in ((3 - 1)..(5 + 2)).rev() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/tests/ui/reversed_empty_ranges_loops_unfixable.rs new file mode 100644 index 00000000000..c4c57224416 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.rs @@ -0,0 +1,11 @@ +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + for i in 5..5 { + println!("{}", i); + } + + for i in (5 + 2)..(8 - 1) { + println!("{}", i); + } +} diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr new file mode 100644 index 00000000000..30095d20cfd --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr @@ -0,0 +1,16 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14 + | +LL | for i in 5..5 { + | ^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14 + | +LL | for i in (5 + 2)..(8 - 1) { + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From e4cd8e7961b553cac44671d63bc6dfc2223ea66b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 12 May 2020 22:05:56 +0200 Subject: [PATCH 0052/1110] Fix ICE caused in unwrap module --- clippy_lints/src/unwrap.rs | 12 +++++++++-- .../ui/checked_unwrap/simple_conditionals.rs | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index f3844c7d3b6..8b971e7064b 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnO use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -90,6 +91,14 @@ fn collect_unwrap_info<'a, 'tcx>( branch: &'tcx Expr<'_>, invert: bool, ) -> Vec> { + fn is_relevant_option_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name) + } + + fn is_relevant_result_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name) + } + 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) => { @@ -106,9 +115,8 @@ fn collect_unwrap_info<'a, 'tcx>( if let ExprKind::MethodCall(method_name, _, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind; let ty = cx.tables.expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) || is_type_diagnostic_item(cx, ty, sym!(result_type)); let name = method_name.ident.as_str(); - if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name); + if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name); then { assert!(args.len() == 1); let unwrappable = match name.as_ref() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 3e7b4b390ba..49794e0c241 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -78,3 +78,24 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern } + +mod issue_5579 { + trait IsErr { + fn is_err(&self, err: &str) -> bool; + } + + impl IsErr for Option { + fn is_err(&self, _err: &str) -> bool { + true + } + } + + #[allow(unused)] + fn boom() { + let t = Some(1); + + if t.is_err("") { + t.unwrap(); + } + } +} From 8d1029d3ca013687422b58d0e99084a4e3421089 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 20:47:44 +0200 Subject: [PATCH 0053/1110] Move test for issue 5579 under tests/ui/crashes --- .../ui/checked_unwrap/simple_conditionals.rs | 21 ------------------- tests/ui/crashes/ice-5579.rs | 17 +++++++++++++++ 2 files changed, 17 insertions(+), 21 deletions(-) create mode 100644 tests/ui/crashes/ice-5579.rs diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 49794e0c241..3e7b4b390ba 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -78,24 +78,3 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern } - -mod issue_5579 { - trait IsErr { - fn is_err(&self, err: &str) -> bool; - } - - impl IsErr for Option { - fn is_err(&self, _err: &str) -> bool { - true - } - } - - #[allow(unused)] - fn boom() { - let t = Some(1); - - if t.is_err("") { - t.unwrap(); - } - } -} diff --git a/tests/ui/crashes/ice-5579.rs b/tests/ui/crashes/ice-5579.rs new file mode 100644 index 00000000000..e1842c73f0e --- /dev/null +++ b/tests/ui/crashes/ice-5579.rs @@ -0,0 +1,17 @@ +trait IsErr { + fn is_err(&self, err: &str) -> bool; +} + +impl IsErr for Option { + fn is_err(&self, _err: &str) -> bool { + true + } +} + +fn main() { + let t = Some(1); + + if t.is_err("") { + t.unwrap(); + } +} From 671c1e34cc11767aa4ea257b9f5c40dcee1441fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 21:07:13 +0200 Subject: [PATCH 0054/1110] Avoid running doctest that is expected to panic --- clippy_lints/src/ranges.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 86d55ccabb6..83c6faac041 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -98,7 +98,7 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust + /// ```rust,no_run /// fn main() { /// (10..=0).for_each(|x| println!("{}", x)); /// From 9217675c7f6ccd2efa18047ebb0c86841683b6a5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 14 May 2020 00:26:09 +0200 Subject: [PATCH 0055/1110] Fix comparison_chain false positive --- clippy_lints/src/comparison_chain.rs | 17 +++++-- tests/ui/comparison_chain.rs | 66 ++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 96df3ffe3ce..93e29edcaa5 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -81,12 +81,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain { // Check that both sets of operands are equal let mut spanless_eq = SpanlessEq::new(cx); - if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2)) - && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2)) - { + let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2); + let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2); + + if !same_fixed_operands && !same_transposed_operands { return; } + // Check that if the operation is the same, either it's not `==` or the operands are transposed + if kind1.node == kind2.node { + if kind1.node == BinOpKind::Eq { + return; + } + if !same_transposed_operands { + return; + } + } + // Check that the type being compared implements `core::cmp::Ord` let ty = cx.tables.expr_ty(lhs1); let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 9c2128469de..3b03f8c7dfe 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -137,4 +137,70 @@ fn h(x: T, y: T, z: T) { } } +// The following uses should be ignored +mod issue_5212 { + use super::{a, b, c}; + fn foo() -> u8 { + 21 + } + + fn same_operation_equals() { + // operands are fixed + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } else { + c() + } + + // operands are transposed + + if foo() == 42 { + a() + } else if 42 == foo() { + b() + } + } + + fn same_operation_not_equals() { + // operands are fixed + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } else { + c() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } else { + c() + } + } +} + fn main() {} From 945c9447093a2ca944e70bae125f2af69f8eac16 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 11:20:51 +0200 Subject: [PATCH 0056/1110] Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` lints into `block_in_if_condition` lint --- CHANGELOG.md | 3 +- clippy_lints/src/block_in_if_condition.rs | 53 +++++++++---------- clippy_lints/src/lib.rs | 9 ++-- src/lintlist/mod.rs | 11 +--- tests/ui/block_in_if_condition.fixed | 3 +- tests/ui/block_in_if_condition.rs | 3 +- tests/ui/block_in_if_condition.stderr | 10 ++-- tests/ui/block_in_if_condition_closure.rs | 5 +- tests/ui/block_in_if_condition_closure.stderr | 6 +-- 9 files changed, 42 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b25ef049356..0b270e6acd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1274,8 +1274,7 @@ Released 2018-09-13 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name -[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr -[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt +[`block_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/block_in_if_condition.rs index 9e533eaa32c..8a5e595749f 100644 --- a/clippy_lints/src/block_in_if_condition.rs +++ b/clippy_lints/src/block_in_if_condition.rs @@ -8,43 +8,40 @@ use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks to contain an - /// expression. + /// **What it does:** Checks for `if` conditions that use blocks containing an + /// expression, statements or conditions that use closures with blocks. /// - /// **Why is this bad?** It isn't really Rust style, same as using parentheses - /// to contain expressions. + /// **Why is this bad?** Style, using blocks in the condition makes it hard to read. /// /// **Known problems:** None. /// - /// **Example:** + /// **Examples:** /// ```rust + /// // Bad /// if { true } { /* ... */ } + /// + /// // Good + /// if true { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION_EXPR, - style, - "braces that can be eliminated in conditions, e.g., `if { true } ...`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks containing - /// statements, or conditions that use closures with blocks. /// - /// **Why is this bad?** Using blocks in the condition makes it hard to read. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// if { let x = somefunc(); x } {} /// // or - /// if somefunc(|x| { x == 47 }) {} + /// + /// ```rust + /// # fn somefunc() -> bool { true }; + /// + /// // Bad + /// if { let x = somefunc(); x } { /* ... */ } + /// + /// // Good + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION_STMT, + pub BLOCK_IN_IF_CONDITION, style, - "complex blocks in conditions, e.g., `if { let x = true; x } ...`" + "useless or complex blocks that can be eliminated in conditions" } -declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT]); +declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION]); struct ExVisitor<'a, 'tcx> { found_block: Option<&'tcx Expr<'tcx>>, @@ -72,7 +69,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ - instead, move the block or closure higher and bind it with a `let`"; + instead, move the block or closure higher and bind it with a `let`"; impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { @@ -92,7 +89,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION_EXPR, + BLOCK_IN_IF_CONDITION, cond.span, BRACED_EXPR_MESSAGE, "try", @@ -118,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION_STMT, + BLOCK_IN_IF_CONDITION, expr.span.with_hi(cond.span.hi()), COMPLEX_BLOCK_MESSAGE, "try", @@ -140,7 +137,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut visitor = ExVisitor { found_block: None, cx }; walk_expr(&mut visitor, cond); if let Some(block) = visitor.found_block { - span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE); + span_lint(cx, BLOCK_IN_IF_CONDITION, block.span, COMPLEX_BLOCK_MESSAGE); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..98b696533d8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -507,8 +507,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, &blacklisted_name::BLACKLISTED_NAME, - &block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR, - &block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT, + &block_in_if_condition::BLOCK_IN_IF_CONDITION, &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, @@ -1209,8 +1208,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT), + LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), @@ -1456,8 +1454,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT), + LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..4ae60f7d808 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -74,16 +74,9 @@ pub static ref ALL_LINTS: Vec = vec![ module: "blacklisted_name", }, Lint { - name: "block_in_if_condition_expr", + name: "block_in_if_condition", group: "style", - desc: "braces that can be eliminated in conditions, e.g., `if { true } ...`", - deprecation: None, - module: "block_in_if_condition", - }, - Lint { - name: "block_in_if_condition_stmt", - group: "style", - desc: "complex blocks in conditions, e.g., `if { let x = true; x } ...`", + desc: "useless or complex blocks that can be eliminated in conditions", deprecation: None, module: "block_in_if_condition", }, diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/block_in_if_condition.fixed index 955801e40f9..ae01c6d3042 100644 --- a/tests/ui/block_in_if_condition.fixed +++ b/tests/ui/block_in_if_condition.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/block_in_if_condition.rs index a6ea01d5fc5..88555dc47c2 100644 --- a/tests/ui/block_in_if_condition.rs +++ b/tests/ui/block_in_if_condition.rs @@ -1,6 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/block_in_if_condition.stderr index b0a0a276c89..89e9ad26f49 100644 --- a/tests/ui/block_in_if_condition.stderr +++ b/tests/ui/block_in_if_condition.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition.rs:27:5 + --> $DIR/block_in_if_condition.rs:26:5 | LL | / if { LL | | let x = 3; @@ -7,7 +7,7 @@ LL | | x == 3 LL | | } { | |_____^ | - = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + = note: `-D clippy::block-in-if-condition` implied by `-D warnings` help: try | LL | let res = { @@ -17,15 +17,13 @@ LL | }; if res { | error: omit braces around single expression condition - --> $DIR/block_in_if_condition.rs:38:8 + --> $DIR/block_in_if_condition.rs:37:8 | LL | if { true } { | ^^^^^^^^ help: try: `true` - | - = note: `-D clippy::block-in-if-condition-expr` implied by `-D warnings` error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:47:8 + --> $DIR/block_in_if_condition.rs:46:8 | LL | if true && x == 3 { | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/block_in_if_condition_closure.rs index bac3eda5e7f..87b3fb94daf 100644 --- a/tests/ui/block_in_if_condition_closure.rs +++ b/tests/ui/block_in_if_condition_closure.rs @@ -1,5 +1,4 @@ -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] fn predicate bool, T>(pfn: F, val: T) -> bool { @@ -10,7 +9,7 @@ fn pred_test() { let v = 3; let sky = "blue"; // This is a sneaky case, where the block isn't directly in the condition, - // but is actually nside a closure that the condition is using. + // but is actually inside a closure that the condition is using. // The same principle applies -- add some extra expressions to make sure // linter isn't confused by them. if v == 3 diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/block_in_if_condition_closure.stderr index 86cd24fe763..3df25691c3c 100644 --- a/tests/ui/block_in_if_condition_closure.stderr +++ b/tests/ui/block_in_if_condition_closure.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:19:17 + --> $DIR/block_in_if_condition_closure.rs:18:17 | LL | |x| { | _________________^ @@ -8,10 +8,10 @@ LL | | x == target LL | | }, | |_____________^ | - = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + = note: `-D clippy::block-in-if-condition` implied by `-D warnings` error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:28:13 + --> $DIR/block_in_if_condition_closure.rs:27:13 | LL | |x| { | _____________^ From 6cbdd1e49dbb2355ac1036946a5a635e22023c6f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 12:28:40 +0200 Subject: [PATCH 0057/1110] Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` lints into `map_unwrap` lint --- CHANGELOG.md | 4 +- clippy_lints/src/lib.rs | 8 +- clippy_lints/src/methods/mod.rs | 94 ++++++------------- .../src/methods/option_map_unwrap_or.rs | 6 +- src/lintlist/mod.rs | 28 ++---- ...{option_map_unwrap_or.rs => map_unwrap.rs} | 23 +++-- ...map_unwrap_or.stderr => map_unwrap.stderr} | 49 +++++++--- tests/ui/result_map_unwrap_or_else.rs | 23 ----- tests/ui/result_map_unwrap_or_else.stderr | 27 ------ 9 files changed, 95 insertions(+), 167 deletions(-) rename tests/ui/{option_map_unwrap_or.rs => map_unwrap.rs} (75%) rename tests/ui/{option_map_unwrap_or.stderr => map_unwrap.stderr} (70%) delete mode 100644 tests/ui/result_map_unwrap_or_else.rs delete mode 100644 tests/ui/result_map_unwrap_or_else.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b270e6acd2..28b05044db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1430,6 +1430,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten +[`map_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items @@ -1499,8 +1500,6 @@ Released 2018-09-13 [`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn -[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or -[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option [`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call @@ -1542,7 +1541,6 @@ Released 2018-09-13 [`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn -[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 98b696533d8..c9a2ef49907 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -673,19 +673,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_FLATTEN, + &methods::MAP_UNWRAP, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, - &methods::OPTION_MAP_UNWRAP_OR, - &methods::OPTION_MAP_UNWRAP_OR_ELSE, &methods::OPTION_UNWRAP_USED, &methods::OR_FUN_CALL, &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::RESULT_MAP_UNWRAP_OR_ELSE, &methods::RESULT_UNWRAP_USED, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, @@ -1152,9 +1150,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::OPTION_MAP_UNWRAP_OR), - LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE), - LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE), + LintId::of(&methods::MAP_UNWRAP), LintId::of(&misc::USED_UNDERSCORE_BINDING), LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3676dc5b09d..401298b2d51 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -257,59 +257,40 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)`. + /// **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or + /// `result.map(_).unwrap_or_else(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map_or(_, _)`. + /// **Why is this bad?** Readability, these can be written more concisely (resp.) as + /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. /// /// **Known problems:** The order of the arguments is not in execution order /// - /// **Example:** + /// **Examples:** /// ```rust /// # let x = Some(1); + /// + /// // Bad /// x.map(|a| a + 1).unwrap_or(0); + /// + /// // Good + /// x.map_or(0, |a| a + 1); /// ``` - pub OPTION_MAP_UNWRAP_OR, - pedantic, - "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).unwrap_or_else(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map_or_else(_, _)`. + /// // or /// - /// **Known problems:** The order of the arguments is not in execution order. - /// - /// **Example:** - /// ```rust - /// # let x = Some(1); - /// # fn some_function() -> usize { 1 } - /// x.map(|a| a + 1).unwrap_or_else(some_function); - /// ``` - pub OPTION_MAP_UNWRAP_OR_ELSE, - pedantic, - "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for usage of `result.map(_).unwrap_or_else(_)`. - /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `result.map_or_else(_, _)`. - /// - /// **Known problems:** None. - /// - /// **Example:** /// ```rust /// # let x: Result = Ok(1); /// # fn some_function(foo: ()) -> usize { 1 } + /// + /// // Bad /// x.map(|a| a + 1).unwrap_or_else(some_function); + /// + /// // Good + /// x.map_or_else(some_function, |a| a + 1); /// ``` - pub RESULT_MAP_UNWRAP_OR_ELSE, + pub MAP_UNWRAP, pedantic, - "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`" + "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" } declare_clippy_lint! { @@ -1294,9 +1275,7 @@ declare_lint_pass!(Methods => [ WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, OK_EXPECT, - OPTION_MAP_UNWRAP_OR, - OPTION_MAP_UNWRAP_OR_ELSE, - RESULT_MAP_UNWRAP_OR_ELSE, + MAP_UNWRAP, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, OPTION_AND_THEN_SOME, @@ -1503,9 +1482,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { cx, lint, first_arg.pat.span, - &format!( - "methods called `{}` usually take {}; consider choosing a less \ - ambiguous name", + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", conv, &self_kinds .iter() @@ -1678,7 +1655,7 @@ fn lint_or_fun_call<'a, 'tcx>( let self_ty = cx.tables.expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); + know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); if poss.contains(&name); @@ -1931,7 +1908,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir: CLONE_DOUBLE_REF, expr.span, "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + this will copy the reference instead of cloning the inner type", |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -2121,7 +2098,7 @@ fn lint_iter_cloned_collect<'a, 'tcx>( ITER_CLONED_COLLECT, to_replace, "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", + more readable", "try", ".to_vec()".to_string(), Applicability::MachineApplicable, @@ -2436,7 +2413,7 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hi None, &format!( "if you don't want to handle the `{}` case gracefully, consider \ - using `expect()` to provide a better panic message", + using `expect()` to provide a better panic message", none_value, ), ); @@ -2494,7 +2471,7 @@ fn lint_map_flatten<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr< // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; + This is more succinctly expressed by calling `.flat_map(..)`"; let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); @@ -2555,10 +2532,10 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( // lint message let msg = if is_option { "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(g, f)` instead" + `map_or_else(g, f)` instead" } else { "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(g, f)` instead" + `.map_or_else(g, f)` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2570,11 +2547,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( if same_span && !multiline { span_lint_and_note( cx, - if is_option { - OPTION_MAP_UNWRAP_OR_ELSE - } else { - RESULT_MAP_UNWRAP_OR_ELSE - }, + MAP_UNWRAP, expr.span, msg, None, @@ -2584,16 +2557,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( ), ); } else if same_span && multiline { - span_lint( - cx, - if is_option { - OPTION_MAP_UNWRAP_OR_ELSE - } else { - RESULT_MAP_UNWRAP_OR_ELSE - }, - expr.span, - msg, - ); + span_lint(cx, MAP_UNWRAP, expr.span, msg); }; } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index bf9dd3c9369..fcaa9b47e64 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use super::OPTION_MAP_UNWRAP_OR; +use super::MAP_UNWRAP; /// lint use of `map().unwrap_or()` for `Option`s pub(super) fn lint<'a, 'tcx>( @@ -62,11 +62,11 @@ pub(super) fn lint<'a, 'tcx>( }; let msg = &format!( "called `map(f).unwrap_or({})` on an `Option` value. \ - This can be done more directly by calling `{}` instead", + This can be done more directly by calling `{}` instead", arg, suggest ); - span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |diag| { + span_lint_and_then(cx, MAP_UNWRAP, expr.span, msg, |diag| { let map_arg_span = map_args[1].span; let mut suggestion = vec![ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4ae60f7d808..d3bd9f66e38 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1137,6 +1137,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "map_unwrap", + group: "pedantic", + desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", + deprecation: None, + module: "methods", + }, Lint { name: "match_as_ref", group: "complexity", @@ -1613,20 +1620,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "option_map_unwrap_or", - group: "pedantic", - desc: "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`", - deprecation: None, - module: "methods", - }, - Lint { - name: "option_map_unwrap_or_else", - group: "pedantic", - desc: "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`", - deprecation: None, - module: "methods", - }, Lint { name: "option_option", group: "pedantic", @@ -1900,13 +1893,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "result_map_unwrap_or_else", - group: "pedantic", - desc: "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`", - deprecation: None, - module: "methods", - }, Lint { name: "result_unwrap_used", group: "restriction", diff --git a/tests/ui/option_map_unwrap_or.rs b/tests/ui/map_unwrap.rs similarity index 75% rename from tests/ui/option_map_unwrap_or.rs rename to tests/ui/map_unwrap.rs index 0364d83663a..53e50368231 100644 --- a/tests/ui/option_map_unwrap_or.rs +++ b/tests/ui/map_unwrap.rs @@ -1,21 +1,18 @@ // FIXME: Add "run-rustfix" once it's supported for multipart suggestions // aux-build:option_helpers.rs -#![warn(clippy::option_map_unwrap_or, clippy::option_map_unwrap_or_else)] +#![warn(clippy::map_unwrap)] #[macro_use] extern crate option_helpers; use std::collections::HashMap; -/// Checks implementation of the following lints: -/// * `OPTION_MAP_UNWRAP_OR` -/// * `OPTION_MAP_UNWRAP_OR_ELSE` #[rustfmt::skip] fn option_methods() { let opt = Some(1); - // Check `OPTION_MAP_UNWRAP_OR`. + // Check for `option.map(_).unwrap_or(_)` use. // Single line case. let _ = opt.map(|x| x + 1) // Should lint even though this call is on a separate line. @@ -49,7 +46,7 @@ fn option_methods() { let id: String = "identifier".to_string(); let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - // Check OPTION_MAP_UNWRAP_OR_ELSE + // Check for `option.map(_).unwrap_or_else(_)` use. // single line case let _ = opt.map(|x| x + 1) // Should lint even though this call is on a separate line. @@ -83,6 +80,20 @@ fn option_methods() { } } +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint +} + fn main() { option_methods(); + result_methods(); } diff --git a/tests/ui/option_map_unwrap_or.stderr b/tests/ui/map_unwrap.stderr similarity index 70% rename from tests/ui/option_map_unwrap_or.stderr rename to tests/ui/map_unwrap.stderr index f05f2893de2..2610923275d 100644 --- a/tests/ui/option_map_unwrap_or.stderr +++ b/tests/ui/map_unwrap.stderr @@ -1,5 +1,5 @@ error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap.rs:17:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -7,14 +7,14 @@ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or(0); | |_____________________^ | - = note: `-D clippy::option-map-unwrap-or` implied by `-D warnings` + = note: `-D clippy::map-unwrap` implied by `-D warnings` help: use `map_or(a, f)` instead | LL | let _ = opt.map_or(0, |x| x + 1); | ^^^^^^ ^^ -- error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:24:13 + --> $DIR/map_unwrap.rs:21:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -32,7 +32,7 @@ LL | ); | error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:28:13 + --> $DIR/map_unwrap.rs:25:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -49,7 +49,7 @@ LL | }, |x| x + 1); | error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/option_map_unwrap_or.rs:33:13 + --> $DIR/map_unwrap.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/option_map_unwrap_or.rs:35:13 + --> $DIR/map_unwrap.rs:32:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -78,7 +78,7 @@ LL | ); | error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/option_map_unwrap_or.rs:39:13 + --> $DIR/map_unwrap.rs:36:13 | LL | let _ = opt | _____________^ @@ -92,7 +92,7 @@ LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/option_map_unwrap_or.rs:54:13 + --> $DIR/map_unwrap.rs:51:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -111,11 +111,10 @@ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or_else(|| 0); | |_____________________________^ | - = note: `-D clippy::option-map-unwrap-or-else` implied by `-D warnings` = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/option_map_unwrap_or.rs:58:13 + --> $DIR/map_unwrap.rs:55:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -125,7 +124,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/option_map_unwrap_or.rs:62:13 + --> $DIR/map_unwrap.rs:59:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -134,5 +133,29 @@ LL | | 0 LL | | ); | |_________^ -error: aborting due to 10 previous errors +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:88:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:90:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:91:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: aborting due to 13 previous errors diff --git a/tests/ui/result_map_unwrap_or_else.rs b/tests/ui/result_map_unwrap_or_else.rs deleted file mode 100644 index 40751bfebe6..00000000000 --- a/tests/ui/result_map_unwrap_or_else.rs +++ /dev/null @@ -1,23 +0,0 @@ -// aux-build:option_helpers.rs - -//! Checks implementation of `RESULT_MAP_UNWRAP_OR_ELSE` - -#![warn(clippy::result_map_unwrap_or_else)] - -#[macro_use] -extern crate option_helpers; - -fn result_methods() { - let res: Result = Ok(1); - - // Check RESULT_MAP_UNWRAP_OR_ELSE - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint -} - -fn main() {} diff --git a/tests/ui/result_map_unwrap_or_else.stderr b/tests/ui/result_map_unwrap_or_else.stderr deleted file mode 100644 index ec7bc8f1241..00000000000 --- a/tests/ui/result_map_unwrap_or_else.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:15:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::result-map-unwrap-or-else` implied by `-D warnings` - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:17:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:18:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: aborting due to 3 previous errors - From bcf61666bd903c0d13c081cf222b423e45cd854e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 13:11:18 +0200 Subject: [PATCH 0058/1110] Merge `option_unwrap_used` and `result_unwrap_used` lints into `unwrap_used` lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +-- clippy_lints/src/methods/mod.rs | 68 ++++++++++++--------------------- src/lintlist/mod.rs | 14 +++---- tests/ui/unwrap.rs | 2 +- tests/ui/unwrap.stderr | 3 +- 6 files changed, 37 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b05044db6..78f98bba2b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1501,7 +1501,6 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`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 -[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`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 @@ -1622,6 +1621,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9a2ef49907..bb3bc0b4545 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -680,11 +680,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OPTION_AS_REF_DEREF, &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, - &methods::OPTION_UNWRAP_USED, &methods::OR_FUN_CALL, &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::RESULT_UNWRAP_USED, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, @@ -695,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, &methods::WRONG_SELF_CONVENTION, @@ -1090,9 +1089,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), LintId::of(&methods::OPTION_EXPECT_USED), - LintId::of(&methods::OPTION_UNWRAP_USED), LintId::of(&methods::RESULT_EXPECT_USED), - LintId::of(&methods::RESULT_UNWRAP_USED), + LintId::of(&methods::UNWRAP_USED), LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), LintId::of(&misc::FLOAT_CMP_CONST), LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 401298b2d51..1af4d03c7a2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -33,40 +33,15 @@ use crate::utils::{ }; declare_clippy_lint! { - /// **What it does:** Checks for `.unwrap()` calls on `Option`s. + /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// - /// **Why is this bad?** Usually it is better to handle the `None` case, or to - /// at least call `.expect(_)` with a more helpful message. Still, for a lot of + /// **Why is this bad?** It is better to handle the `None` or `Err` case, + /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is /// `Allow` by default. /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// Using unwrap on an `Option`: - /// - /// ```rust - /// let opt = Some(1); - /// opt.unwrap(); - /// ``` - /// - /// Better: - /// - /// ```rust - /// let opt = Some(1); - /// opt.expect("more helpful message"); - /// ``` - pub OPTION_UNWRAP_USED, - restriction, - "using `Option.unwrap()`, which should at least get a better message using `expect()`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `.unwrap()` calls on `Result`s. - /// - /// **Why is this bad?** `result.unwrap()` will let the thread panic on `Err` - /// values. Normally, you want to implement more sophisticated error handling, + /// `result.unwrap()` will let the thread panic on `Err` values. + /// Normally, you want to implement more sophisticated error handling, /// and propagate errors upwards with `?` operator. /// /// Even if you want to panic on errors, not all `Error`s implement good @@ -75,23 +50,31 @@ declare_clippy_lint! { /// /// **Known problems:** None. /// - /// **Example:** - /// Using unwrap on an `Result`: - /// + /// **Examples:** /// ```rust - /// let res: Result = Ok(1); - /// res.unwrap(); + /// # let opt = Some(1); + /// + /// // Bad + /// opt.unwrap(); + /// + /// // Good + /// opt.expect("more helpful message"); /// ``` /// - /// Better: + /// // or /// /// ```rust - /// let res: Result = Ok(1); + /// # let res: Result = Ok(1); + /// + /// // Bad + /// res.unwrap(); + /// + /// // Good /// res.expect("more helpful message"); /// ``` - pub RESULT_UNWRAP_USED, + pub UNWRAP_USED, restriction, - "using `Result.unwrap()`, which might be better handled" + "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`" } declare_clippy_lint! { @@ -1267,8 +1250,7 @@ declare_clippy_lint! { } declare_lint_pass!(Methods => [ - OPTION_UNWRAP_USED, - RESULT_UNWRAP_USED, + UNWRAP_USED, OPTION_EXPECT_USED, RESULT_EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, @@ -2397,9 +2379,9 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hi let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&unwrap_args[0])); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { - Some((OPTION_UNWRAP_USED, "an Option", "None")) + Some((UNWRAP_USED, "an Option", "None")) } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { - Some((RESULT_UNWRAP_USED, "a Result", "Err")) + Some((UNWRAP_USED, "a Result", "Err")) } else { None }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d3bd9f66e38..5cbf3ef028c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1627,13 +1627,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, - Lint { - name: "option_unwrap_used", - group: "restriction", - desc: "using `Option.unwrap()`, which should at least get a better message using `expect()`", - deprecation: None, - module: "methods", - }, Lint { name: "or_fun_call", group: "perf", @@ -2404,6 +2397,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "returns", }, + Lint { + name: "unwrap_used", + group: "restriction", + desc: "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`", + deprecation: None, + module: "methods", + }, Lint { name: "use_debug", group: "restriction", diff --git a/tests/ui/unwrap.rs b/tests/ui/unwrap.rs index fcd1fcd14d4..a4a3cd1d379 100644 --- a/tests/ui/unwrap.rs +++ b/tests/ui/unwrap.rs @@ -1,4 +1,4 @@ -#![warn(clippy::option_unwrap_used, clippy::result_unwrap_used)] +#![warn(clippy::unwrap_used)] fn unwrap_option() { let opt = Some(0); diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index b90ce68fa97..4f0858005f6 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -4,7 +4,7 @@ error: used `unwrap()` on `an Option` value LL | let _ = opt.unwrap(); | ^^^^^^^^^^^^ | - = note: `-D clippy::option-unwrap-used` implied by `-D warnings` + = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: used `unwrap()` on `a Result` value @@ -13,7 +13,6 @@ error: used `unwrap()` on `a Result` value LL | let _ = res.unwrap(); | ^^^^^^^^^^^^ | - = note: `-D clippy::result-unwrap-used` implied by `-D warnings` = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message error: aborting due to 2 previous errors From 0e8be599cd04a8566224c63eeb07f5fa04605702 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 13:32:17 +0200 Subject: [PATCH 0059/1110] Merge `option_expect_used` and `result_expect_used` lints into `expect_used` lint --- CHANGELOG.md | 3 +- clippy_lints/src/lib.rs | 6 +-- clippy_lints/src/methods/mod.rs | 69 +++++++++++++-------------------- src/lintlist/mod.rs | 21 ++++------ tests/ui/expect.rs | 2 +- tests/ui/expect.stderr | 3 +- 6 files changed, 38 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78f98bba2b4..4eeb71fa5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1337,6 +1337,7 @@ Released 2018-09-13 [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used [`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy [`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop [`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods @@ -1497,7 +1498,6 @@ Released 2018-09-13 [`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap -[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`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 @@ -1537,7 +1537,6 @@ Released 2018-09-13 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs -[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bb3bc0b4545..eaef1f543d3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -657,6 +657,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, &methods::EXPECT_FUN_CALL, + &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, &methods::FILTER_MAP, &methods::FILTER_MAP_NEXT, @@ -678,10 +679,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, - &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, - &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, @@ -1086,10 +1085,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(&mem_forget::MEM_FORGET), LintId::of(&methods::CLONE_ON_REF_PTR), + LintId::of(&methods::EXPECT_USED), LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), - LintId::of(&methods::OPTION_EXPECT_USED), - LintId::of(&methods::RESULT_EXPECT_USED), LintId::of(&methods::UNWRAP_USED), LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), LintId::of(&misc::FLOAT_CMP_CONST), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1af4d03c7a2..2e75de019b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -78,61 +78,45 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `.expect()` calls on `Option`s. + /// **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s. /// - /// **Why is this bad?** Usually it is better to handle the `None` case. Still, - /// for a lot of quick-and-dirty code, `expect` is a good choice, which is why - /// this lint is `Allow` by default. + /// **Why is this bad?** Usually it is better to handle the `None` or `Err` case. + /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why + /// this lint is `Allow` by default. /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// Using expect on an `Option`: - /// - /// ```rust - /// let opt = Some(1); - /// opt.expect("one"); - /// ``` - /// - /// Better: - /// - /// ```rust,ignore - /// let opt = Some(1); - /// opt?; - /// ``` - pub OPTION_EXPECT_USED, - restriction, - "using `Option.expect()`, which might be better handled" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `.expect()` calls on `Result`s. - /// - /// **Why is this bad?** `result.expect()` will let the thread panic on `Err` + /// `result.expect()` will let the thread panic on `Err` /// values. Normally, you want to implement more sophisticated error handling, /// and propagate errors upwards with `?` operator. /// /// **Known problems:** None. /// - /// **Example:** - /// Using expect on an `Result`: + /// **Examples:** + /// ```rust,ignore + /// # let opt = Some(1); /// - /// ```rust - /// let res: Result = Ok(1); - /// res.expect("one"); + /// // Bad + /// opt.expect("one"); + /// + /// // Good + /// let opt = Some(1); + /// opt?; /// ``` /// - /// Better: + /// // or /// /// ```rust - /// let res: Result = Ok(1); + /// # let res: Result = Ok(1); + /// + /// // Bad + /// res.expect("one"); + /// + /// // Good /// res?; /// # Ok::<(), ()>(()) /// ``` - pub RESULT_EXPECT_USED, + pub EXPECT_USED, restriction, - "using `Result.expect()`, which might be better handled" + "using `.expect()` on `Result` or `Option`, which might be better handled" } declare_clippy_lint! { @@ -1251,8 +1235,7 @@ declare_clippy_lint! { declare_lint_pass!(Methods => [ UNWRAP_USED, - OPTION_EXPECT_USED, - RESULT_EXPECT_USED, + EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, @@ -2407,9 +2390,9 @@ fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, expect_args: &[hi let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0])); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { - Some((OPTION_EXPECT_USED, "an Option", "None")) + Some((EXPECT_USED, "an Option", "None")) } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { - Some((RESULT_EXPECT_USED, "a Result", "Err")) + Some((EXPECT_USED, "a Result", "Err")) } else { None }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5cbf3ef028c..4e79ce96bb5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -514,6 +514,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "expect_used", + group: "restriction", + desc: "using `.expect()` on `Result` or `Option`, which might be better handled", + deprecation: None, + module: "methods", + }, Lint { name: "expl_impl_clone_on_copy", group: "pedantic", @@ -1599,13 +1606,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "option_env_unwrap", }, - Lint { - name: "option_expect_used", - group: "restriction", - desc: "using `Option.expect()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "option_map_or_none", group: "style", @@ -1865,13 +1865,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, - Lint { - name: "result_expect_used", - group: "restriction", - desc: "using `Result.expect()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "result_map_or_into_option", group: "style", diff --git a/tests/ui/expect.rs b/tests/ui/expect.rs index 0bd4252c49a..1073acf6f0c 100644 --- a/tests/ui/expect.rs +++ b/tests/ui/expect.rs @@ -1,4 +1,4 @@ -#![warn(clippy::option_expect_used, clippy::result_expect_used)] +#![warn(clippy::expect_used)] fn expect_option() { let opt = Some(0); diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index adf9f4f1921..9d3fc7df15c 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -4,7 +4,7 @@ error: used `expect()` on `an Option` value LL | let _ = opt.expect(""); | ^^^^^^^^^^^^^^ | - = note: `-D clippy::option-expect-used` implied by `-D warnings` + = note: `-D clippy::expect-used` implied by `-D warnings` = help: if this value is an `None`, it will panic error: used `expect()` on `a Result` value @@ -13,7 +13,6 @@ error: used `expect()` on `a Result` value LL | let _ = res.expect(""); | ^^^^^^^^^^^^^^ | - = note: `-D clippy::result-expect-used` implied by `-D warnings` = help: if this value is an `Err`, it will panic error: aborting due to 2 previous errors From adbdf7549c6b24c37629eabdc4be0346e0c8fd56 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 15:16:00 +0200 Subject: [PATCH 0060/1110] Merge `for_loop_over_option` and `for_loop_over_result` lints into `for_loop_over_fallible` lint --- CHANGELOG.md | 3 +- clippy_lints/src/lib.rs | 9 +-- clippy_lints/src/loops.rs | 76 ++++++++----------- src/lintlist/mod.rs | 11 +-- ...on_result.rs => for_loop_over_fallible.rs} | 10 +-- ...t.stderr => for_loop_over_fallible.stderr} | 19 +++-- 6 files changed, 52 insertions(+), 76 deletions(-) rename tests/ui/{for_loop_over_option_result.rs => for_loop_over_fallible.rs} (81%) rename tests/ui/{for_loop_over_option_result.stderr => for_loop_over_fallible.stderr} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eeb71fa5c5..3f9486e0972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1361,8 +1361,7 @@ Released 2018-09-13 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map -[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option -[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result +[`for_loop_over_fallible`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_fallible [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index eaef1f543d3..8de94d19d31 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -615,8 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_INTO_ITER_LOOP, &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, - &loops::FOR_LOOP_OVER_OPTION, - &loops::FOR_LOOP_OVER_RESULT, + &loops::FOR_LOOP_OVER_FALLIBLE, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -1265,8 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOP_OVER_OPTION), - LintId::of(&loops::FOR_LOOP_OVER_RESULT), + LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1641,8 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&loops::FOR_LOOP_OVER_OPTION), - LintId::of(&loops::FOR_LOOP_OVER_RESULT), + LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0bc6b70855b..da6793a69d6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -168,7 +168,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `for` loops over `Option` values. + /// **What it does:** Checks for `for` loops over `Option` or `Result` values. /// /// **Why is this bad?** Readability. This is more clearly expressed as an `if /// let`. @@ -176,47 +176,38 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```ignore - /// for x in option { - /// .. + /// ```rust + /// # let opt = Some(1); + /// + /// // Bad + /// for x in opt { + /// // .. + /// } + /// + /// // Good + /// if let Some(x) = opt { + /// // .. /// } /// ``` /// - /// This should be - /// ```ignore - /// if let Some(x) = option { - /// .. + /// // or + /// + /// ```rust + /// # let res: Result = Ok(1); + /// + /// // Bad + /// for x in &res { + /// // .. + /// } + /// + /// // Good + /// if let Ok(x) = res { + /// // .. /// } /// ``` - pub FOR_LOOP_OVER_OPTION, + pub FOR_LOOP_OVER_FALLIBLE, correctness, - "for-looping over an `Option`, which is more clearly expressed as an `if let`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `for` loops over `Result` values. - /// - /// **Why is this bad?** Readability. This is more clearly expressed as an `if - /// let`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```ignore - /// for x in result { - /// .. - /// } - /// ``` - /// - /// This should be - /// ```ignore - /// if let Ok(x) = result { - /// .. - /// } - /// ``` - pub FOR_LOOP_OVER_RESULT, - correctness, - "for-looping over a `Result`, which is more clearly expressed as an `if let`" + "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } declare_clippy_lint! { @@ -435,8 +426,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOP_OVER_RESULT, - FOR_LOOP_OVER_OPTION, + FOR_LOOP_OVER_FALLIBLE, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -1283,7 +1273,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e ITER_NEXT_LOOP, expr.span, "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ - probably not what you want", + probably not what you want", ); next_loop_linted = true; } @@ -1300,11 +1290,11 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { if is_type_diagnostic_item(cx, ty, sym!(option_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_OPTION, + FOR_LOOP_OVER_FALLIBLE, arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement.", + `if let` statement.", snippet(cx, arg.span, "_") ), None, @@ -1317,11 +1307,11 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_RESULT, + FOR_LOOP_OVER_FALLIBLE, arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement.", + `if let` statement.", snippet(cx, arg.span, "_") ), None, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4e79ce96bb5..0ea0f55a381 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -676,16 +676,9 @@ pub static ref ALL_LINTS: Vec = vec![ module: "loops", }, Lint { - name: "for_loop_over_option", + name: "for_loop_over_fallible", group: "correctness", - desc: "for-looping over an `Option`, which is more clearly expressed as an `if let`", - deprecation: None, - module: "loops", - }, - Lint { - name: "for_loop_over_result", - group: "correctness", - desc: "for-looping over a `Result`, which is more clearly expressed as an `if let`", + desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", deprecation: None, module: "loops", }, diff --git a/tests/ui/for_loop_over_option_result.rs b/tests/ui/for_loop_over_fallible.rs similarity index 81% rename from tests/ui/for_loop_over_option_result.rs rename to tests/ui/for_loop_over_fallible.rs index 6b207b26b6b..e52468cdd4b 100644 --- a/tests/ui/for_loop_over_option_result.rs +++ b/tests/ui/for_loop_over_fallible.rs @@ -1,18 +1,16 @@ -#![warn(clippy::for_loop_over_option, clippy::for_loop_over_result)] +#![warn(clippy::for_loop_over_fallible)] -/// Tests for_loop_over_result and for_loop_over_option - -fn for_loop_over_option_and_result() { +fn for_loop_over_fallible() { let option = Some(1); let result = option.ok_or("x not found"); let v = vec![0, 1, 2]; - // check FOR_LOOP_OVER_OPTION lint + // check over an `Option` for x in option { println!("{}", x); } - // check FOR_LOOP_OVER_RESULT lint + // check over a `Result` for x in result { println!("{}", x); } diff --git a/tests/ui/for_loop_over_option_result.stderr b/tests/ui/for_loop_over_fallible.stderr similarity index 81% rename from tests/ui/for_loop_over_option_result.stderr rename to tests/ui/for_loop_over_fallible.stderr index 194a0bfec5b..4ce9a144ad8 100644 --- a/tests/ui/for_loop_over_option_result.stderr +++ b/tests/ui/for_loop_over_fallible.stderr @@ -1,23 +1,22 @@ error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:11:14 + --> $DIR/for_loop_over_fallible.rs:9:14 | LL | for x in option { | ^^^^^^ | - = note: `-D clippy::for-loop-over-option` implied by `-D warnings` + = note: `-D clippy::for-loop-over-fallible` implied by `-D warnings` = help: consider replacing `for x in option` with `if let Some(x) = option` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:16:14 + --> $DIR/for_loop_over_fallible.rs:14:14 | LL | for x in result { | ^^^^^^ | - = note: `-D clippy::for-loop-over-result` implied by `-D warnings` = help: consider replacing `for x in result` with `if let Ok(x) = result` error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:20:14 + --> $DIR/for_loop_over_fallible.rs:18:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +24,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_over_option_result.rs:26:14 + --> $DIR/for_loop_over_fallible.rs:24:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -33,7 +32,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:31:14 + --> $DIR/for_loop_over_fallible.rs:29:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +40,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:35:14 + --> $DIR/for_loop_over_fallible.rs:33:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +48,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loop_over_option_result.rs:47:5 + --> $DIR/for_loop_over_fallible.rs:45:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -60,7 +59,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loop_over_option_result.rs:53:5 + --> $DIR/for_loop_over_fallible.rs:51:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); From 95399f8f941b89785c6e9d94e0bc32ff5d43ba06 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 14 May 2020 09:57:36 -0700 Subject: [PATCH 0061/1110] Downgrade useless_let_if_seq to nursery --- clippy_lints/src/let_if_seq.rs | 2 +- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 398a3103a03..d7bf8a14768 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// }; /// ``` pub USELESS_LET_IF_SEQ, - style, + nursery, "unidiomatic `let mut` declaration followed by initialization in `if`" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..b241ac5559c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1266,7 +1266,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1476,7 +1475,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1728,6 +1726,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), LintId::of(&future_not_send::FUTURE_NOT_SEND), + LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), 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_INTEGER), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..e1c68b58b86 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2469,7 +2469,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "useless_let_if_seq", - group: "style", + group: "nursery", desc: "unidiomatic `let mut` declaration followed by initialization in `if`", deprecation: None, module: "let_if_seq", From 94e4b5ec316993200d75276b4e7c16a059bf3a57 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 00:08:41 +0300 Subject: [PATCH 0062/1110] Add the redundant_wildcard_enum_match lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/matches.rs | 48 +++++++++++++++++++ src/lintlist/mod.rs | 7 +++ .../match_wildcard_for_single_variants.fixed | 18 +++++++ .../ui/match_wildcard_for_single_variants.rs | 18 +++++++ .../match_wildcard_for_single_variants.stderr | 10 ++++ 7 files changed, 104 insertions(+) create mode 100644 tests/ui/match_wildcard_for_single_variants.fixed create mode 100644 tests/ui/match_wildcard_for_single_variants.rs create mode 100644 tests/ui/match_wildcard_for_single_variants.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b25ef049356..d6298fec65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1439,6 +1439,7 @@ Released 2018-09-13 [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter [`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..41046c18ed2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -641,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, &matches::MATCH_SINGLE_BINDING, + &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, &matches::SINGLE_MATCH, @@ -1147,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f86535ef1e..42a6c416619 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -229,6 +229,40 @@ declare_clippy_lint! { "a wildcard enum match arm using `_`" } +declare_clippy_lint! { + /// **What it does:** Checks for wildcard enum matches for a single variant. + /// + /// **Why is this bad?** New enum variants added by library updates can be missed. + /// + /// **Known problems:** Suggested replacements may not use correct path to enum + /// if it's not present in the current scope. + /// + /// **Example:** + /// + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// Foo::C => {}, + /// } + /// ``` + pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + pedantic, + "a wildcard enum match for a single variant" +} + declare_clippy_lint! { /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm. /// @@ -356,6 +390,7 @@ impl_lint_pass!(Matches => [ MATCH_WILD_ERR_ARM, MATCH_AS_REF, WILDCARD_ENUM_MATCH_ARM, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, INFALLIBLE_DESTRUCTURING_MATCH, @@ -766,6 +801,19 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ } } + if suggestion.len() == 1 { + // No need to check for non-exhaustive enum as in that case len would be greater than 1 + span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + message, + "try this", + suggestion[0].clone(), + Applicability::MachineApplicable, + ) + }; + span_lint_and_sugg( cx, WILDCARD_ENUM_MATCH_ARM, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..250a7c09f78 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1200,6 +1200,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, + Lint { + name: "match_wildcard_for_single_variants", + group: "pedantic", + desc: "a wildcard enum match for a single variant", + deprecation: None, + module: "matches", + }, Lint { name: "maybe_infinite_iter", group: "pedantic", diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed new file mode 100644 index 00000000000..5f1a559f591 --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +fn main() { + match Foo::A { + Foo::A => {}, + Foo::B => {}, + Foo::C => {}, + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs new file mode 100644 index 00000000000..1159f9e722d --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +fn main() { + match Foo::A { + Foo::A => {}, + Foo::B => {}, + _ => {}, + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr new file mode 100644 index 00000000000..128dd4808bf --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -0,0 +1,10 @@ +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:16:9 + | +LL | _ => {}, + | ^ help: try this: `Foo::C` + | + = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` + +error: aborting due to previous error + From 0ad9f7d651b52de4be6384c9b6dc893b389fd557 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 18:33:12 +0300 Subject: [PATCH 0063/1110] Fix trivial cases of new match_wildcard_for_single_variants lint --- clippy_lints/src/float_literal.rs | 2 +- clippy_lints/src/misc_early.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/trivially_copy_pass_by_ref.rs | 2 +- tests/compile-test.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 3a52b1d3fc2..4c604cd0107 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -77,7 +77,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral { let type_suffix = match lit_float_ty { LitFloatType::Suffixed(FloatTy::F32) => Some("f32"), LitFloatType::Suffixed(FloatTy::F64) => Some("f64"), - _ => None + LitFloatType::Unsuffixed => None }; let (is_whole, mut float_str) = match fty { FloatTy::F32 => { diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 62ee051624b..552222eba2e 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -379,7 +379,7 @@ impl EarlyLintPass for MiscEarlyLints { let left_binding = match left { BindingMode::ByRef(Mutability::Mut) => "ref mut ", BindingMode::ByRef(Mutability::Not) => "ref ", - _ => "", + BindingMode::ByValue(..) => "", }; if let PatKind::Wild = right.kind { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 4301157e164..9cfc8d19134 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { return; } }, - _ => return, + FnKind::Closure(..) => return, } let mir = cx.tcx.optimized_mir(def_id); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index a21818701da..c099c553333 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { } }, FnKind::Method(..) => (), - _ => return, + FnKind::Closure(..) => return, } // Exclude non-inherent impls diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 2c101220c5d..8e0cb94317a 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef { } }, FnKind::Method(..) => (), - _ => return, + FnKind::Closure(..) => return, } // Exclude non-inherent impls diff --git a/tests/compile-test.rs b/tests/compile-test.rs index de2cf6d7873..a3df9d5ccbd 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -44,7 +44,7 @@ fn third_party_crates() -> String { for entry in fs::read_dir(dep_dir).unwrap() { let path = match entry { Ok(entry) => entry.path(), - _ => continue, + Err(_) => continue, }; if let Some(name) = path.file_name().and_then(OsStr::to_str) { for dep in CRATES { From 494830797744c09d6de3b2b2452ab185d2204005 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 18:34:29 +0300 Subject: [PATCH 0064/1110] Fix cases of match_wildcard_for_single_variants lint when it is spanned on Option --- clippy_lints/src/consts.rs | 9 +++++---- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/loops.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 1 + clippy_lints/src/utils/mod.rs | 2 ++ 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 81ddc8c0067..efb424bcb7b 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -139,6 +139,7 @@ impl Constant { .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + #[allow(clippy::match_wildcard_for_single_variants)] match Self::partial_cmp(tcx, cmp_type, lv, rv) { Some(Equal) => Some(ls.cmp(rs)), x => x, @@ -354,14 +355,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - _ => None, + Some(_) | None => None, }, (Some(Constant::Vec(vec)), _) => { if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { match vec.get(0) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - _ => None, + Some(_) | None => None, } } else { None @@ -532,7 +533,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - _ => None, + Some(_) | None => None, }, ty::Float(FloatTy::F64) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc @@ -546,7 +547,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - _ => None, + Some(_) | None => None, }, // FIXME: implement other array type conversions. _ => None, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 1ec60a0e6e6..615afee33ef 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -95,12 +95,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { match map.find(id) { Some(Node::Binding(_)) => (), - _ => return false, + Some(_) | None => return false, } match map.find(map.get_parent_node(id)) { Some(Node::Param(_)) => true, - _ => false, + Some(_) | None => false, } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 86317fb8bd5..8c61b2f8664 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -410,7 +410,7 @@ fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { Some(Constant::Int(i)) => i == 0, Some(Constant::F32(f)) => f == 0.0, Some(Constant::F64(f)) => f == 0.0, - _ => false, + Some(_) | None => false, } } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0bc6b70855b..39908bff5ed 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2154,7 +2154,7 @@ fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Ex } }, Some(Node::Stmt(_)) => (), - _ => { + Some(_) | None => { return false; }, } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index e1d524c2231..38c2645d36e 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -509,7 +509,7 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> boo Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), _ => false, }), - _ => false, + Some(_) | None => false, } } diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 4ca90455bc4..3bb3eb15d9c 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -37,6 +37,7 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + #[allow(clippy::match_wildcard_for_single_variants)] match constant(cx, cx.tables, operand) { Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind { ty::Int(ity) => { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3b8ef18bfab..7bc8be492e8 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -370,6 +370,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O /// Checks whether this type implements `Drop`. pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + #[allow(clippy::match_wildcard_for_single_variants)] match ty.ty_adt_def() { Some(def) => def.has_dtor(cx.tcx), _ => false, @@ -444,6 +445,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); + #[allow(clippy::match_wildcard_for_single_variants)] match cx.tcx.hir().find(parent_id) { Some( Node::Item(Item { ident, .. }) From 749619cfe34be1ee591f3af748fbdd4d2f54d3f0 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Thu, 14 May 2020 22:40:33 +0300 Subject: [PATCH 0065/1110] Apply suggestions from PR review --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 42a6c416619..444f5bb0db6 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -810,7 +810,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ message, "try this", suggestion[0].clone(), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) }; @@ -821,7 +821,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ message, "try this", suggestion.join(" | "), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) } } From 1c59cd5f2110ff90a256f0948f05716403e84b85 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Thu, 14 May 2020 22:41:05 +0300 Subject: [PATCH 0066/1110] Fix example code of wildcard_enum_match_arm lint --- clippy_lints/src/matches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 444f5bb0db6..6fdb4cf9cd7 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -220,7 +220,7 @@ declare_clippy_lint! { /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); /// match x { - /// A => {}, + /// Foo::A(_) => {}, /// _ => {}, /// } /// ``` From 93386563f66823ac7d10641c007b0bbc23ab09e6 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 19:58:27 +0200 Subject: [PATCH 0067/1110] Rename lint `map_unwrap` to `map_unwrap_or` and register lints as renamed --- CHANGELOG.md | 3 +- clippy_lints/src/lib.rs | 15 ++++++++-- clippy_lints/src/methods/mod.rs | 8 +++--- .../src/methods/option_map_unwrap_or.rs | 4 +-- src/lintlist/mod.rs | 9 +----- tests/ui/{map_unwrap.rs => map_unwrap_or.rs} | 2 +- ...map_unwrap.stderr => map_unwrap_or.stderr} | 28 +++++++++---------- 7 files changed, 36 insertions(+), 33 deletions(-) rename tests/ui/{map_unwrap.rs => map_unwrap_or.rs} (98%) rename tests/ui/{map_unwrap.stderr => map_unwrap_or.stderr} (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f9486e0972..77272f4f78b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1430,7 +1430,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten -[`map_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items @@ -1538,7 +1538,6 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn -[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8de94d19d31..ff67ccae794 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -673,7 +673,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_FLATTEN, - &methods::MAP_UNWRAP, + &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, @@ -1145,7 +1145,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::MAP_UNWRAP), + LintId::of(&methods::MAP_UNWRAP_OR), LintId::of(&misc::USED_UNDERSCORE_BINDING), LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), @@ -1785,6 +1785,17 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loop_over_fallible"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loop_over_fallible"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2e75de019b6..e6094edc5d7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -255,7 +255,7 @@ declare_clippy_lint! { /// // Good /// x.map_or_else(some_function, |a| a + 1); /// ``` - pub MAP_UNWRAP, + pub MAP_UNWRAP_OR, pedantic, "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" } @@ -1240,7 +1240,7 @@ declare_lint_pass!(Methods => [ WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, OK_EXPECT, - MAP_UNWRAP, + MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, OPTION_AND_THEN_SOME, @@ -2512,7 +2512,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( if same_span && !multiline { span_lint_and_note( cx, - MAP_UNWRAP, + MAP_UNWRAP_OR, expr.span, msg, None, @@ -2522,7 +2522,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( ), ); } else if same_span && multiline { - span_lint(cx, MAP_UNWRAP, expr.span, msg); + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); }; } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index fcaa9b47e64..20c60ef3318 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use super::MAP_UNWRAP; +use super::MAP_UNWRAP_OR; /// lint use of `map().unwrap_or()` for `Option`s pub(super) fn lint<'a, 'tcx>( @@ -66,7 +66,7 @@ pub(super) fn lint<'a, 'tcx>( arg, suggest ); - span_lint_and_then(cx, MAP_UNWRAP, expr.span, msg, |diag| { + span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { let map_arg_span = map_args[1].span; let mut suggestion = vec![ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0ea0f55a381..e90b9c15747 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1138,7 +1138,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "map_unwrap", + name: "map_unwrap_or", group: "pedantic", desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", deprecation: None, @@ -1872,13 +1872,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "result_unwrap_used", - group: "restriction", - desc: "using `Result.unwrap()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/map_unwrap.rs b/tests/ui/map_unwrap_or.rs similarity index 98% rename from tests/ui/map_unwrap.rs rename to tests/ui/map_unwrap_or.rs index 53e50368231..585944032e7 100644 --- a/tests/ui/map_unwrap.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,7 +1,7 @@ // FIXME: Add "run-rustfix" once it's supported for multipart suggestions // aux-build:option_helpers.rs -#![warn(clippy::map_unwrap)] +#![warn(clippy::map_unwrap_or)] #[macro_use] extern crate option_helpers; diff --git a/tests/ui/map_unwrap.stderr b/tests/ui/map_unwrap_or.stderr similarity index 90% rename from tests/ui/map_unwrap.stderr rename to tests/ui/map_unwrap_or.stderr index 2610923275d..b62080a073f 100644 --- a/tests/ui/map_unwrap.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:17:13 + --> $DIR/map_unwrap_or.rs:17:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -7,14 +7,14 @@ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or(0); | |_____________________^ | - = note: `-D clippy::map-unwrap` implied by `-D warnings` + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` help: use `map_or(a, f)` instead | LL | let _ = opt.map_or(0, |x| x + 1); | ^^^^^^ ^^ -- error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:21:13 + --> $DIR/map_unwrap_or.rs:21:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -32,7 +32,7 @@ LL | ); | error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:25:13 + --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -49,7 +49,7 @@ LL | }, |x| x + 1); | error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/map_unwrap.rs:30:13 + --> $DIR/map_unwrap_or.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/map_unwrap.rs:32:13 + --> $DIR/map_unwrap_or.rs:32:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -78,7 +78,7 @@ LL | ); | error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/map_unwrap.rs:36:13 + --> $DIR/map_unwrap_or.rs:36:13 | LL | let _ = opt | _____________^ @@ -92,7 +92,7 @@ LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:47:13 + --> $DIR/map_unwrap_or.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:51:13 + --> $DIR/map_unwrap_or.rs:51:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -114,7 +114,7 @@ LL | | .unwrap_or_else(|| 0); = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:55:13 + --> $DIR/map_unwrap_or.rs:55:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -124,7 +124,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:59:13 + --> $DIR/map_unwrap_or.rs:59:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -134,7 +134,7 @@ LL | | ); | |_________^ error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:88:13 + --> $DIR/map_unwrap_or.rs:88:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even t = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:90:13 + --> $DIR/map_unwrap_or.rs:90:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -150,7 +150,7 @@ LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:91:13 + --> $DIR/map_unwrap_or.rs:91:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From ab87f87ba03518da23ca510249aa3f5908a42368 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 15 May 2020 18:20:07 +0200 Subject: [PATCH 0068/1110] Fix CHANGELOG.md and lint names plural --- CHANGELOG.md | 22 ++++++++--------- ...ondition.rs => blocks_in_if_conditions.rs} | 12 +++++----- clippy_lints/src/lib.rs | 24 +++++++++---------- clippy_lints/src/loops.rs | 8 +++---- src/lintlist/mod.rs | 6 ++--- ...on.fixed => blocks_in_if_conditions.fixed} | 2 +- ...ondition.rs => blocks_in_if_conditions.rs} | 2 +- ....stderr => blocks_in_if_conditions.stderr} | 8 +++---- ....rs => blocks_in_if_conditions_closure.rs} | 2 +- ...=> blocks_in_if_conditions_closure.stderr} | 6 ++--- ...allible.rs => for_loops_over_fallibles.rs} | 4 ++-- ...stderr => for_loops_over_fallibles.stderr} | 18 +++++++------- 12 files changed, 57 insertions(+), 57 deletions(-) rename clippy_lints/src/{block_in_if_condition.rs => blocks_in_if_conditions.rs} (93%) rename tests/ui/{block_in_if_condition.fixed => blocks_in_if_conditions.fixed} (96%) rename tests/ui/{block_in_if_condition.rs => blocks_in_if_conditions.rs} (96%) rename tests/ui/{block_in_if_condition.stderr => blocks_in_if_conditions.stderr} (77%) rename tests/ui/{block_in_if_condition_closure.rs => blocks_in_if_conditions_closure.rs} (95%) rename tests/ui/{block_in_if_condition_closure.stderr => blocks_in_if_conditions_closure.stderr} (78%) rename tests/ui/{for_loop_over_fallible.rs => for_loops_over_fallibles.rs} (93%) rename tests/ui/{for_loop_over_fallible.stderr => for_loops_over_fallibles.stderr} (84%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77272f4f78b..d05819a973a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,7 +198,7 @@ Released 2020-03-12 ### Suggestion Improvements -* [`option_map_unwrap_or`] [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) * [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934) * [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935) * [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956) @@ -282,8 +282,8 @@ Released 2019-12-19 * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) - * [`option_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) - * [`result_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509) * Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736) * Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613) @@ -395,7 +395,7 @@ Released 2019-08-15 * Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107) * Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214) * Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136) -* Improve suggestions for [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) * Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119) * Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137) * Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071) @@ -448,7 +448,7 @@ Released 2019-05-20 * Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()` * Fix false positive in [`bool_comparison`] pertaining to non-bool types * Fix false positive in [`redundant_closure`] pertaining to differences in borrows -* Fix false positive in [`option_map_unwrap_or`] on non-copy types +* Fix false positive in `option_map_unwrap_or` on non-copy types * Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls * Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros * Fix false positive in [`needless_continue`] pertaining to loop labels @@ -794,7 +794,7 @@ Released 2018-09-13 ## 0.0.169 * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* -* New lints: [`just_underscores_and_digits`], [`result_map_unwrap_or_else`], [`transmute_bytes_to_str`] +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* @@ -1068,7 +1068,7 @@ Released 2018-09-13 ## 0.0.93 — 2016-10-03 * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* -* [`option_map_unwrap_or`] and [`option_map_unwrap_or_else`] are now +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] @@ -1087,8 +1087,8 @@ Released 2018-09-13 ## 0.0.88 — 2016-09-04 * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` - lint groups: [`filter_next`], [`for_loop_over_option`], - [`for_loop_over_result`] and [`match_overlapping_arm`]. You should now be + lint groups: [`filter_next`], `for_loop_over_option`, + `for_loop_over_result` and [`match_overlapping_arm`]. You should now be able to `#[allow/deny]` them individually and they are available directly through `cargo clippy`. @@ -1274,7 +1274,7 @@ Released 2018-09-13 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name -[`block_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box @@ -1361,7 +1361,7 @@ Released 2018-09-13 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map -[`for_loop_over_fallible`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_fallible +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/blocks_in_if_conditions.rs similarity index 93% rename from clippy_lints/src/block_in_if_condition.rs rename to clippy_lints/src/blocks_in_if_conditions.rs index 8a5e595749f..8fa9b05ca32 100644 --- a/clippy_lints/src/block_in_if_condition.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -36,12 +36,12 @@ declare_clippy_lint! { /// let res = { let x = somefunc(); x }; /// if res { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION, + pub BLOCKS_IN_IF_CONDITIONS, style, "useless or complex blocks that can be eliminated in conditions" } -declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION]); +declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); struct ExVisitor<'a, 'tcx> { found_block: Option<&'tcx Expr<'tcx>>, @@ -71,7 +71,7 @@ const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression conditio const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ instead, move the block or closure higher and bind it with a `let`"; -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlocksInIfConditions { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) { return; @@ -89,7 +89,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION, + BLOCKS_IN_IF_CONDITIONS, cond.span, BRACED_EXPR_MESSAGE, "try", @@ -115,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION, + BLOCKS_IN_IF_CONDITIONS, expr.span.with_hi(cond.span.hi()), COMPLEX_BLOCK_MESSAGE, "try", @@ -137,7 +137,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut visitor = ExVisitor { found_block: None, cx }; walk_expr(&mut visitor, cond); if let Some(block) = visitor.found_block { - span_lint(cx, BLOCK_IN_IF_CONDITION, block.span, COMPLEX_BLOCK_MESSAGE); + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ff67ccae794..eba4ab5056b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -180,7 +180,7 @@ mod attrs; mod await_holding_lock; mod bit_mask; mod blacklisted_name; -mod block_in_if_condition; +mod blocks_in_if_conditions; mod booleans; mod bytecount; mod cargo_common_metadata; @@ -507,7 +507,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, &blacklisted_name::BLACKLISTED_NAME, - &block_in_if_condition::BLOCK_IN_IF_CONDITION, + &blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, @@ -615,7 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_INTO_ITER_LOOP, &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, - &loops::FOR_LOOP_OVER_FALLIBLE, + &loops::FOR_LOOPS_OVER_FALLIBLES, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -894,7 +894,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed); store.register_late_pass(|| box len_zero::LenZero); store.register_late_pass(|| box attrs::Attributes); - store.register_late_pass(|| box block_in_if_condition::BlockInIfCondition); + store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); @@ -1199,7 +1199,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), @@ -1264,7 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1444,7 +1444,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), @@ -1639,7 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), @@ -1785,8 +1785,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::block_in_if_condition"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); @@ -1794,8 +1794,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loop_over_fallible"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loop_over_fallible"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index da6793a69d6..9c9d1a84003 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -205,7 +205,7 @@ declare_clippy_lint! { /// // .. /// } /// ``` - pub FOR_LOOP_OVER_FALLIBLE, + pub FOR_LOOPS_OVER_FALLIBLES, correctness, "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } @@ -426,7 +426,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -1290,7 +1290,7 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { if is_type_diagnostic_item(cx, ty, sym!(option_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ @@ -1307,7 +1307,7 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e90b9c15747..feada261a4c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -74,11 +74,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "blacklisted_name", }, Lint { - name: "block_in_if_condition", + name: "blocks_in_if_conditions", group: "style", desc: "useless or complex blocks that can be eliminated in conditions", deprecation: None, - module: "block_in_if_condition", + module: "blocks_in_if_conditions", }, Lint { name: "bool_comparison", @@ -676,7 +676,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "loops", }, Lint { - name: "for_loop_over_fallible", + name: "for_loops_over_fallibles", group: "correctness", desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", deprecation: None, diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/blocks_in_if_conditions.fixed similarity index 96% rename from tests/ui/block_in_if_condition.fixed rename to tests/ui/blocks_in_if_conditions.fixed index ae01c6d3042..9040552cefc 100644 --- a/tests/ui/block_in_if_condition.fixed +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition)] +#![warn(clippy::blocks_in_if_conditions)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/blocks_in_if_conditions.rs similarity index 96% rename from tests/ui/block_in_if_condition.rs rename to tests/ui/blocks_in_if_conditions.rs index 88555dc47c2..2fe409b22d3 100644 --- a/tests/ui/block_in_if_condition.rs +++ b/tests/ui/blocks_in_if_conditions.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition)] +#![warn(clippy::blocks_in_if_conditions)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/blocks_in_if_conditions.stderr similarity index 77% rename from tests/ui/block_in_if_condition.stderr rename to tests/ui/blocks_in_if_conditions.stderr index 89e9ad26f49..9bdddc8e152 100644 --- a/tests/ui/block_in_if_condition.stderr +++ b/tests/ui/blocks_in_if_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition.rs:26:5 + --> $DIR/blocks_in_if_conditions.rs:26:5 | LL | / if { LL | | let x = 3; @@ -7,7 +7,7 @@ LL | | x == 3 LL | | } { | |_____^ | - = note: `-D clippy::block-in-if-condition` implied by `-D warnings` + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` help: try | LL | let res = { @@ -17,13 +17,13 @@ LL | }; if res { | error: omit braces around single expression condition - --> $DIR/block_in_if_condition.rs:37:8 + --> $DIR/blocks_in_if_conditions.rs:37:8 | LL | if { true } { | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:46:8 + --> $DIR/blocks_in_if_conditions.rs:46:8 | LL | if true && x == 3 { | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/blocks_in_if_conditions_closure.rs similarity index 95% rename from tests/ui/block_in_if_condition_closure.rs rename to tests/ui/blocks_in_if_conditions_closure.rs index 87b3fb94daf..acbabfa20d7 100644 --- a/tests/ui/block_in_if_condition_closure.rs +++ b/tests/ui/blocks_in_if_conditions_closure.rs @@ -1,4 +1,4 @@ -#![warn(clippy::block_in_if_condition)] +#![warn(clippy::blocks_in_if_conditions)] #![allow(unused, clippy::let_and_return)] fn predicate bool, T>(pfn: F, val: T) -> bool { diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/blocks_in_if_conditions_closure.stderr similarity index 78% rename from tests/ui/block_in_if_condition_closure.stderr rename to tests/ui/blocks_in_if_conditions_closure.stderr index 3df25691c3c..941d604dd5f 100644 --- a/tests/ui/block_in_if_condition_closure.stderr +++ b/tests/ui/blocks_in_if_conditions_closure.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:18:17 + --> $DIR/blocks_in_if_conditions_closure.rs:18:17 | LL | |x| { | _________________^ @@ -8,10 +8,10 @@ LL | | x == target LL | | }, | |_____________^ | - = note: `-D clippy::block-in-if-condition` implied by `-D warnings` + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:27:13 + --> $DIR/blocks_in_if_conditions_closure.rs:27:13 | LL | |x| { | _____________^ diff --git a/tests/ui/for_loop_over_fallible.rs b/tests/ui/for_loops_over_fallibles.rs similarity index 93% rename from tests/ui/for_loop_over_fallible.rs rename to tests/ui/for_loops_over_fallibles.rs index e52468cdd4b..1b9dde87cd5 100644 --- a/tests/ui/for_loop_over_fallible.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,6 +1,6 @@ -#![warn(clippy::for_loop_over_fallible)] +#![warn(clippy::for_loops_over_fallibles)] -fn for_loop_over_fallible() { +fn for_loops_over_fallibles() { let option = Some(1); let result = option.ok_or("x not found"); let v = vec![0, 1, 2]; diff --git a/tests/ui/for_loop_over_fallible.stderr b/tests/ui/for_loops_over_fallibles.stderr similarity index 84% rename from tests/ui/for_loop_over_fallible.stderr rename to tests/ui/for_loops_over_fallibles.stderr index 4ce9a144ad8..bef228d4b93 100644 --- a/tests/ui/for_loop_over_fallible.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -1,14 +1,14 @@ error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:9:14 + --> $DIR/for_loops_over_fallibles.rs:9:14 | LL | for x in option { | ^^^^^^ | - = note: `-D clippy::for-loop-over-fallible` implied by `-D warnings` + = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` = help: consider replacing `for x in option` with `if let Some(x) = option` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:14:14 + --> $DIR/for_loops_over_fallibles.rs:14:14 | LL | for x in result { | ^^^^^^ @@ -16,7 +16,7 @@ LL | for x in result { = help: consider replacing `for x in result` with `if let Ok(x) = result` error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:18:14 + --> $DIR/for_loops_over_fallibles.rs:18:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_over_fallible.rs:24:14 + --> $DIR/for_loops_over_fallibles.rs:24:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:29:14 + --> $DIR/for_loops_over_fallibles.rs:29:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:33:14 + --> $DIR/for_loops_over_fallibles.rs:33:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loop_over_fallible.rs:45:5 + --> $DIR/for_loops_over_fallibles.rs:45:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -59,7 +59,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loop_over_fallible.rs:51:5 + --> $DIR/for_loops_over_fallibles.rs:51:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); From fc8ab099c38952b91e38608c386314bde6dd2629 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 15 May 2020 21:17:37 +0200 Subject: [PATCH 0069/1110] identity_op: allow `1 << 0` --- clippy_lints/src/identity_op.rs | 22 ++++++++++++++++++++-- tests/ui/identity_op.rs | 5 +++++ tests/ui/identity_op.stderr | 20 +++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 088e4ab1921..78e07d25f67 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,4 +1,5 @@ -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use if_chain::if_chain; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -32,7 +33,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if is_allowed(cx, cmp, left, right) { + return; + } match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { check(cx, left, 0, e.span, right.span); @@ -54,6 +58,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp { } } +fn is_allowed(cx: &LateContext<'_, '_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { + // `1 << 0` is a common pattern in bit manipulation code + if_chain! { + if let BinOpKind::Shl = cmp.node; + if let Some(Constant::Int(0)) = constant_simple(cx, cx.tables, right); + if let Some(Constant::Int(1)) = constant_simple(cx, cx.tables, left); + then { + return true; + } + } + + false +} + #[allow(clippy::cast_possible_wrap)] fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.tables, e) { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index ae2815d345a..ceaacaaf6bd 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -33,4 +33,9 @@ fn main() { let u: u8 = 0; u & 255; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42 << 0; + 1 >> 0; + 42 >> 0; } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 4742877706a..d8d44a74f9a 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -48,5 +48,23 @@ error: the operation is ineffective. Consider reducing it to `u` LL | u & 255; | ^^^^^^^ -error: aborting due to 8 previous errors +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:38:5 + | +LL | 42 << 0; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:39:5 + | +LL | 1 >> 0; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:40:5 + | +LL | 42 >> 0; + | ^^^^^^^ + +error: aborting due to 11 previous errors From 10313a2631efa6a01dc86199d554ce5a7c1bb51a Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Fri, 15 May 2020 22:33:37 +0300 Subject: [PATCH 0070/1110] Revert "Fix cases of match_wildcard_for_single_variants lint when it is spanned on Option" This reverts commit 494830797744c09d6de3b2b2452ab185d2204005. --- clippy_lints/src/consts.rs | 9 ++++----- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/loops.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 1 - clippy_lints/src/utils/mod.rs | 2 -- 7 files changed, 9 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index efb424bcb7b..81ddc8c0067 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -139,7 +139,6 @@ impl Constant { .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { - #[allow(clippy::match_wildcard_for_single_variants)] match Self::partial_cmp(tcx, cmp_type, lv, rv) { Some(Equal) => Some(ls.cmp(rs)), x => x, @@ -355,14 +354,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - Some(_) | None => None, + _ => None, }, (Some(Constant::Vec(vec)), _) => { if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { match vec.get(0) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - Some(_) | None => None, + _ => None, } } else { None @@ -533,7 +532,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - Some(_) | None => None, + _ => None, }, ty::Float(FloatTy::F64) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc @@ -547,7 +546,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - Some(_) | None => None, + _ => None, }, // FIXME: implement other array type conversions. _ => None, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 615afee33ef..1ec60a0e6e6 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -95,12 +95,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { match map.find(id) { Some(Node::Binding(_)) => (), - Some(_) | None => return false, + _ => return false, } match map.find(map.get_parent_node(id)) { Some(Node::Param(_)) => true, - Some(_) | None => false, + _ => false, } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 8c61b2f8664..86317fb8bd5 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -410,7 +410,7 @@ fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { Some(Constant::Int(i)) => i == 0, Some(Constant::F32(f)) => f == 0.0, Some(Constant::F64(f)) => f == 0.0, - Some(_) | None => false, + _ => false, } } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 39908bff5ed..0bc6b70855b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2154,7 +2154,7 @@ fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Ex } }, Some(Node::Stmt(_)) => (), - Some(_) | None => { + _ => { return false; }, } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 38c2645d36e..e1d524c2231 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -509,7 +509,7 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> boo Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), _ => false, }), - Some(_) | None => false, + _ => false, } } diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 3bb3eb15d9c..4ca90455bc4 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -37,7 +37,6 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { - #[allow(clippy::match_wildcard_for_single_variants)] match constant(cx, cx.tables, operand) { Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind { ty::Int(ity) => { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 7bc8be492e8..3b8ef18bfab 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -370,7 +370,6 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O /// Checks whether this type implements `Drop`. pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - #[allow(clippy::match_wildcard_for_single_variants)] match ty.ty_adt_def() { Some(def) => def.has_dtor(cx.tcx), _ => false, @@ -445,7 +444,6 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); - #[allow(clippy::match_wildcard_for_single_variants)] match cx.tcx.hir().find(parent_id) { Some( Node::Item(Item { ident, .. }) From 2620d2449da851171773f7bec1396af11babe278 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sat, 16 May 2020 00:06:52 +0300 Subject: [PATCH 0071/1110] Fix check for missing enum variants from match expressions TupleStruct matches are checked for exhaustiveness --- clippy_lints/src/matches.rs | 16 ++++++++++++++-- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 6fdb4cf9cd7..7d722820800 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -764,9 +764,21 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ if let QPath::Resolved(_, p) = path { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } - } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind { + } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind { if let QPath::Resolved(_, p) = path { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + // Some simple checks for exhaustive patterns. + // There is a room for improvements to detect more cases, + // but it can be more expensive to do so. + let is_pattern_exhaustive = |pat: &&Pat<'_>| { + if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { + true + } else { + false + } + }; + if patterns.iter().all(is_pattern_exhaustive) { + missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + } } } } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3b8ef18bfab..7545235e646 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -372,7 +372,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.ty_adt_def() { Some(def) => def.has_dtor(cx.tcx), - _ => false, + None => false, } } From d90625385e8ed0a9030e3ab2ea0990fce39c28bf Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sat, 16 May 2020 00:19:30 +0300 Subject: [PATCH 0072/1110] Add more test cases for match_wildcard_for_single_variants --- .../match_wildcard_for_single_variants.fixed | 43 ++++++++++++++++++- .../ui/match_wildcard_for_single_variants.rs | 43 ++++++++++++++++++- .../match_wildcard_for_single_variants.stderr | 22 +++++++++- 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index 5f1a559f591..519200977a7 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -9,10 +9,51 @@ enum Foo { C, } +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), +} + fn main() { - match Foo::A { + let f = Foo::A; + match f { Foo::A => {}, Foo::B => {}, Foo::C => {}, } + + let color = Color::Red; + + // check exhaustive bindings + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_r, _g, _b) => {}, + Color::Blue => {}, + } + + // check exhaustive wild + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(..) => {}, + Color::Blue => {}, + } + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_, _, _) => {}, + Color::Blue => {}, + } + + // shouldn't lint as there is one missing variant + // and one that isn't exhaustively covered + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(255, _, _) => {}, + _ => {}, + } } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1159f9e722d..1df917e085c 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -9,10 +9,51 @@ enum Foo { C, } +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), +} + fn main() { - match Foo::A { + let f = Foo::A; + match f { Foo::A => {}, Foo::B => {}, _ => {}, } + + let color = Color::Red; + + // check exhaustive bindings + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_r, _g, _b) => {}, + _ => {}, + } + + // check exhaustive wild + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(..) => {}, + _ => {}, + } + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_, _, _) => {}, + _ => {}, + } + + // shouldn't lint as there is one missing variant + // and one that isn't exhaustively covered + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(255, _, _) => {}, + _ => {}, + } } diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 128dd4808bf..82790aa9e80 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,10 +1,28 @@ error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:16:9 + --> $DIR/match_wildcard_for_single_variants.rs:24:9 | LL | _ => {}, | ^ help: try this: `Foo::C` | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: aborting due to previous error +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:34:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:42:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:48:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: aborting due to 4 previous errors From e55b920970fdc33f5ddaf7757738fbacdadf15ab Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 4 May 2020 17:09:02 +0200 Subject: [PATCH 0073/1110] Rename lint `identity_conversion` to `useless_conversion` --- CHANGELOG.md | 4 +- clippy_lints/src/lib.rs | 11 +++-- ...ty_conversion.rs => useless_conversion.rs} | 30 +++++++----- src/lintlist/mod.rs | 14 +++--- ...version.fixed => useless_conversion.fixed} | 4 +- ...ty_conversion.rs => useless_conversion.rs} | 4 +- ...rsion.stderr => useless_conversion.stderr} | 46 +++++++++---------- 7 files changed, 60 insertions(+), 53 deletions(-) rename clippy_lints/src/{identity_conversion.rs => useless_conversion.rs} (84%) rename tests/ui/{identity_conversion.fixed => useless_conversion.fixed} (93%) rename tests/ui/{identity_conversion.rs => useless_conversion.rs} (94%) rename tests/ui/{identity_conversion.stderr => useless_conversion.stderr} (67%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d05819a973a..9e85e6da3b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -805,7 +805,7 @@ Released 2018-09-13 ## 0.0.166 * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* -* New lints: [`explicit_write`], [`identity_conversion`], [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] @@ -1367,7 +1367,6 @@ Released 2018-09-13 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap -[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion [`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op [`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex [`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching @@ -1624,6 +1623,7 @@ Released 2018-09-13 [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bda0d5c0458..4dda373738b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -221,7 +221,6 @@ mod formatting; mod functions; mod future_not_send; mod get_last_with_len; -mod identity_conversion; mod identity_op; mod if_let_mutex; mod if_let_some_result; @@ -324,6 +323,7 @@ mod unused_io_amount; mod unused_self; mod unwrap; mod use_self; +mod useless_conversion; mod vec; mod verbose_file_reads; mod wildcard_dependencies; @@ -577,7 +577,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &functions::TOO_MANY_LINES, &future_not_send::FUTURE_NOT_SEND, &get_last_with_len::GET_LAST_WITH_LEN, - &identity_conversion::IDENTITY_CONVERSION, &identity_op::IDENTITY_OP, &if_let_mutex::IF_LET_MUTEX, &if_let_some_result::IF_LET_SOME_RESULT, @@ -843,6 +842,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, &use_self::USE_SELF, + &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, @@ -980,7 +980,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); - store.register_late_pass(|| box identity_conversion::IdentityConversion::default()); + store.register_late_pass(|| box useless_conversion::UselessConversion::default()); store.register_late_pass(|| box types::ImplicitHasher); store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); store.register_late_pass(|| box types::UnitArg); @@ -1241,7 +1241,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_conversion::IDENTITY_CONVERSION), LintId::of(&identity_op::IDENTITY_OP), LintId::of(&if_let_mutex::IF_LET_MUTEX), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), @@ -1427,6 +1426,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), @@ -1546,7 +1546,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&format::USELESS_FORMAT), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_conversion::IDENTITY_CONVERSION), LintId::of(&identity_op::IDENTITY_OP), LintId::of(&int_plus_one::INT_PLUS_ONE), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -1605,6 +1604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); @@ -1795,6 +1795,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/identity_conversion.rs b/clippy_lints/src/useless_conversion.rs similarity index 84% rename from clippy_lints/src/identity_conversion.rs rename to clippy_lints/src/useless_conversion.rs index 33a9478f058..95921518986 100644 --- a/clippy_lints/src/identity_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -7,30 +7,36 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions. + /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts + /// to the same type as caller. /// /// **Why is this bad?** Redundant code. /// /// **Known problems:** None. /// /// **Example:** + /// /// ```rust + /// // Bad /// // format!() returns a `String` /// let s: String = format!("hello").into(); + /// + /// // Good + /// let s: String = format!("hello"); /// ``` - pub IDENTITY_CONVERSION, + pub USELESS_CONVERSION, complexity, - "using always-identical `Into`/`From`/`IntoIter` conversions" + "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" } #[derive(Default)] -pub struct IdentityConversion { +pub struct UselessConversion { try_desugar_arm: Vec, } -impl_lint_pass!(IdentityConversion => [IDENTITY_CONVERSION]); +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { return; @@ -60,9 +66,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { span_lint_and_sugg( cx, - IDENTITY_CONVERSION, + USELESS_CONVERSION, e.span, - "identical conversion", + "useless conversion", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -76,9 +82,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { let sugg = snippet(cx, args[0].span, "").into_owned(); span_lint_and_sugg( cx, - IDENTITY_CONVERSION, + USELESS_CONVERSION, e.span, - "identical conversion", + "useless conversion", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -99,9 +105,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( cx, - IDENTITY_CONVERSION, + USELESS_CONVERSION, e.span, - "identical conversion", + "useless conversion", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e5e3bf453a0..e411e60782a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -717,13 +717,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, - Lint { - name: "identity_conversion", - group: "complexity", - desc: "using always-identical `Into`/`From`/`IntoIter` conversions", - deprecation: None, - module: "identity_conversion", - }, Lint { name: "identity_op", group: "complexity", @@ -2418,6 +2411,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "useless_conversion", + group: "complexity", + desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + deprecation: None, + module: "useless_conversion", + }, Lint { name: "useless_format", group: "complexity", diff --git a/tests/ui/identity_conversion.fixed b/tests/ui/useless_conversion.fixed similarity index 93% rename from tests/ui/identity_conversion.fixed rename to tests/ui/useless_conversion.fixed index dd3fc56e98b..fdd4bc581f3 100644 --- a/tests/ui/identity_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![deny(clippy::identity_conversion)] +#![deny(clippy::useless_conversion)] fn test_generic(val: T) -> T { let _ = val; @@ -41,7 +41,7 @@ fn main() { let _: String = "foo".into(); let _: String = From::from("foo"); let _ = String::from("foo"); - #[allow(clippy::identity_conversion)] + #[allow(clippy::useless_conversion)] { let _: String = "foo".into(); let _ = String::from("foo"); diff --git a/tests/ui/identity_conversion.rs b/tests/ui/useless_conversion.rs similarity index 94% rename from tests/ui/identity_conversion.rs rename to tests/ui/useless_conversion.rs index 875ed7db373..4cae745e7c0 100644 --- a/tests/ui/identity_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,6 +1,6 @@ // run-rustfix -#![deny(clippy::identity_conversion)] +#![deny(clippy::useless_conversion)] fn test_generic(val: T) -> T { let _ = T::from(val); @@ -41,7 +41,7 @@ fn main() { let _: String = "foo".into(); let _: String = From::from("foo"); let _ = String::from("foo"); - #[allow(clippy::identity_conversion)] + #[allow(clippy::useless_conversion)] { let _: String = "foo".into(); let _ = String::from("foo"); diff --git a/tests/ui/identity_conversion.stderr b/tests/ui/useless_conversion.stderr similarity index 67% rename from tests/ui/identity_conversion.stderr rename to tests/ui/useless_conversion.stderr index 57626b23795..7df3507edfd 100644 --- a/tests/ui/identity_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,65 +1,65 @@ -error: identical conversion - --> $DIR/identity_conversion.rs:6:13 +error: useless conversion + --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` | note: the lint level is defined here - --> $DIR/identity_conversion.rs:3:9 + --> $DIR/useless_conversion.rs:3:9 | -LL | #![deny(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: identical conversion - --> $DIR/identity_conversion.rs:7:5 +error: useless conversion + --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: identical conversion - --> $DIR/identity_conversion.rs:19:22 +error: useless conversion + --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: identical conversion - --> $DIR/identity_conversion.rs:51:21 +error: useless conversion + --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: identical conversion - --> $DIR/identity_conversion.rs:52:21 +error: useless conversion + --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: identical conversion - --> $DIR/identity_conversion.rs:53:13 +error: useless conversion + --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: identical conversion - --> $DIR/identity_conversion.rs:54:13 +error: useless conversion + --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: identical conversion - --> $DIR/identity_conversion.rs:55:13 +error: useless conversion + --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: identical conversion - --> $DIR/identity_conversion.rs:56:13 +error: useless conversion + --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: identical conversion - --> $DIR/identity_conversion.rs:57:21 +error: useless conversion + --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` From cb7f9679a63075b3fce2fdc69f7b02fe0f482464 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 25 Apr 2020 20:52:00 +0300 Subject: [PATCH 0074/1110] simplify multispan_sugg interface - add `multispan_sugg_with_applicability` - not it gets `&str` instead of `String`, like in `diag.multispan_suggestion` --- clippy_lints/src/eq_op.rs | 2 +- clippy_lints/src/loops.rs | 4 ++-- clippy_lints/src/matches.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/types.rs | 2 +- clippy_lints/src/utils/diagnostics.rs | 28 +++++++++++----------- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 098d47bdd40..4e1c1f13140 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -115,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp { let rsnip = snippet(cx, r.span, "...").to_string(); multispan_sugg( diag, - "use the values directly".to_string(), + "use the values directly", vec![(left.span, lsnip), (right.span, rsnip)], ); }, diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9c9d1a84003..4a9c411d7c8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1134,7 +1134,7 @@ fn check_for_loop_range<'a, 'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator".to_string(), + "consider using an iterator", vec![ (pat.span, format!("({}, )", ident.name)), ( @@ -1163,7 +1163,7 @@ fn check_for_loop_range<'a, 'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator".to_string(), + "consider using an iterator", vec![(pat.span, "".to_string()), (arg.span, repl)], ); }, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f86535ef1e..bbf14374a1f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -820,7 +820,7 @@ fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_> span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { if !expr.span.from_expansion() { - multispan_sugg(diag, msg.to_owned(), suggs); + multispan_sugg(diag, msg, suggs); } }); } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index a21818701da..ed48ab54897 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -293,7 +293,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { ); spans.sort_by_key(|&(span, _)| span); } - multispan_sugg(diag, "consider taking a reference instead".to_string(), spans); + multispan_sugg(diag, "consider taking a reference instead", spans); }; span_lint_and_then( diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6d49f50d550..f50adbc48ab 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2206,7 +2206,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher { multispan_sugg( diag, - "consider adding a type parameter".to_string(), + "consider adding a type parameter", vec![ ( generics_suggestion_span, diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 24a1bdf1883..f6d87c8532e 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -1,6 +1,6 @@ //! Clippy wrappers around rustc's diagnostic functions. -use rustc_errors::{Applicability, CodeSuggestion, DiagnosticBuilder, Substitution, SubstitutionPart, SuggestionStyle}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::source_map::{MultiSpan, Span}; @@ -198,20 +198,20 @@ pub fn span_lint_and_sugg<'a, T: LintContext>( /// appear once per /// replacement. In human-readable format though, it only appears once before /// the whole suggestion. -pub fn multispan_sugg(diag: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I) +pub fn multispan_sugg(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I) where I: IntoIterator, { - let sugg = CodeSuggestion { - substitutions: vec![Substitution { - parts: sugg - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }], - msg: help_msg, - style: SuggestionStyle::ShowCode, - applicability: Applicability::Unspecified, - }; - diag.suggestions.push(sugg); + multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg) +} + +pub fn multispan_sugg_with_applicability( + diag: &mut DiagnosticBuilder<'_>, + help_msg: &str, + applicability: Applicability, + sugg: I, +) where + I: IntoIterator, +{ + diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability); } From 404ae5b211c9fb3960b64ecbf91d903d484b0c20 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 May 2020 01:14:28 +0200 Subject: [PATCH 0075/1110] Re-remove util/dev Maybe someday, git subtree will do it right --- util/dev | 7 ------- 1 file changed, 7 deletions(-) delete mode 100755 util/dev diff --git a/util/dev b/util/dev deleted file mode 100755 index 319de217e0d..00000000000 --- a/util/dev +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -CARGO_TARGET_DIR=$(pwd)/target/ -export CARGO_TARGET_DIR - -echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.' - -cd clippy_dev && cargo run -- "$@" From 7f317b708fe0889c04b7590ba53f3a41afa44a1d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 May 2020 01:18:43 +0200 Subject: [PATCH 0076/1110] Run fmt --- src/driver.rs | 228 +++++++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 115 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 1ce0300f239..d3a7e24937f 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -295,121 +295,119 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option = env::args().collect(); + exit(rustc_driver::catch_with_exit_code(move || { + let mut orig_args: Vec = env::args().collect(); - if orig_args.iter().any(|a| a == "--version" || a == "-V") { - let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); - exit(0); + if orig_args.iter().any(|a| a == "--version" || a == "-V") { + let version_info = rustc_tools_util::get_version_info!(); + println!("{}", version_info); + exit(0); + } + + // Get the sysroot, looking from most specific to this invocation to the least: + // - command line + // - runtime environment + // - SYSROOT + // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN + // - sysroot from rustc in the path + // - compile-time environment + // - SYSROOT + // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN + let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); + let have_sys_root_arg = sys_root_arg.is_some(); + let sys_root = sys_root_arg + .map(PathBuf::from) + .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from)) + .or_else(|| { + let home = std::env::var("RUSTUP_HOME") + .or_else(|_| std::env::var("MULTIRUST_HOME")) + .ok(); + let toolchain = std::env::var("RUSTUP_TOOLCHAIN") + .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN")) + .ok(); + toolchain_path(home, toolchain) + }) + .or_else(|| { + Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .ok() + .and_then(|out| String::from_utf8(out.stdout).ok()) + .map(|s| PathBuf::from(s.trim())) + }) + .or_else(|| option_env!("SYSROOT").map(PathBuf::from)) + .or_else(|| { + let home = option_env!("RUSTUP_HOME") + .or(option_env!("MULTIRUST_HOME")) + .map(ToString::to_string); + let toolchain = option_env!("RUSTUP_TOOLCHAIN") + .or(option_env!("MULTIRUST_TOOLCHAIN")) + .map(ToString::to_string); + toolchain_path(home, toolchain) + }) + .map(|pb| pb.to_string_lossy().to_string()) + .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); + + // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. + // We're invoking the compiler programmatically, so we ignore this/ + let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); + + if wrapper_mode { + // we still want to be able to invoke it normally though + orig_args.remove(1); + } + + if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) { + display_help(); + exit(0); + } + + let should_describe_lints = || { + let args: Vec<_> = env::args().collect(); + args.windows(2).any(|args| { + args[1] == "help" + && match args[0].as_str() { + "-W" | "-A" | "-D" | "-F" => true, + _ => false, + } + }) + }; + + if !wrapper_mode && should_describe_lints() { + describe_lints(); + exit(0); + } + + // this conditional check for the --sysroot flag is there so users can call + // `clippy_driver` directly + // without having to pass --sysroot or anything + let mut args: Vec = orig_args.clone(); + if !have_sys_root_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + + // this check ensures that dependencies are built but not linted and the final + // crate is linted but not built + let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true") + || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); + + if clippy_enabled { + args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); + if let Ok(extra_args) = env::var("CLIPPY_ARGS") { + args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { + if s.is_empty() { + None + } else { + Some(s.to_string()) + } + })); } - - // Get the sysroot, looking from most specific to this invocation to the least: - // - command line - // - runtime environment - // - SYSROOT - // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN - // - sysroot from rustc in the path - // - compile-time environment - // - SYSROOT - // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN - let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); - let have_sys_root_arg = sys_root_arg.is_some(); - let sys_root = sys_root_arg - .map(PathBuf::from) - .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from)) - .or_else(|| { - let home = std::env::var("RUSTUP_HOME") - .or_else(|_| std::env::var("MULTIRUST_HOME")) - .ok(); - let toolchain = std::env::var("RUSTUP_TOOLCHAIN") - .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN")) - .ok(); - toolchain_path(home, toolchain) - }) - .or_else(|| { - Command::new("rustc") - .arg("--print") - .arg("sysroot") - .output() - .ok() - .and_then(|out| String::from_utf8(out.stdout).ok()) - .map(|s| PathBuf::from(s.trim())) - }) - .or_else(|| option_env!("SYSROOT").map(PathBuf::from)) - .or_else(|| { - let home = option_env!("RUSTUP_HOME") - .or(option_env!("MULTIRUST_HOME")) - .map(ToString::to_string); - let toolchain = option_env!("RUSTUP_TOOLCHAIN") - .or(option_env!("MULTIRUST_TOOLCHAIN")) - .map(ToString::to_string); - toolchain_path(home, toolchain) - }) - .map(|pb| pb.to_string_lossy().to_string()) - .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); - - // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. - // We're invoking the compiler programmatically, so we ignore this/ - let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); - - if wrapper_mode { - // we still want to be able to invoke it normally though - orig_args.remove(1); - } - - if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) { - display_help(); - exit(0); - } - - let should_describe_lints = || { - let args: Vec<_> = env::args().collect(); - args.windows(2).any(|args| { - args[1] == "help" - && match args[0].as_str() { - "-W" | "-A" | "-D" | "-F" => true, - _ => false, - } - }) - }; - - if !wrapper_mode && should_describe_lints() { - describe_lints(); - exit(0); - } - - // this conditional check for the --sysroot flag is there so users can call - // `clippy_driver` directly - // without having to pass --sysroot or anything - let mut args: Vec = orig_args.clone(); - if !have_sys_root_arg { - args.extend(vec!["--sysroot".into(), sys_root]); - }; - - // this check ensures that dependencies are built but not linted and the final - // crate is linted but not built - let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true") - || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); - - if clippy_enabled { - args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); - if let Ok(extra_args) = env::var("CLIPPY_ARGS") { - args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { - if s.is_empty() { - None - } else { - Some(s.to_string()) - } - })); - } - } - let mut clippy = ClippyCallbacks; - let mut default = DefaultCallbacks; - let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = - if clippy_enabled { &mut clippy } else { &mut default }; - rustc_driver::run_compiler(&args, callbacks, None, None) - }) - ) + } + let mut clippy = ClippyCallbacks; + let mut default = DefaultCallbacks; + let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = + if clippy_enabled { &mut clippy } else { &mut default }; + rustc_driver::run_compiler(&args, callbacks, None, None) + })) } From e5b5f6f8a99253560096a5718fc7fc81daa23e02 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sun, 17 May 2020 01:38:01 +0200 Subject: [PATCH 0077/1110] Better explain remotes in the sync process. --- CONTRIBUTING.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7a60938374..6697ff2f40d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -166,15 +166,18 @@ Clippy in the `rust-lang/rust` repository. For general information about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. -Here is a TL;DR version of the sync process: +Here is a TL;DR version of the sync process (all of the following commands have +to be run inside the `rust` directory): -1. Clone the [`rust-lang/rust`] repository (all of the following commands have - to be run inside the `rust` directory) +1. Clone the [`rust-lang/rust`] repository 2. Sync the changes to the rust-copy of Clippy to your Clippy fork: ```bash # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust ``` + _Note:_ This will directly push to the remote repository. You can also push + to your local copy by replacing the remote address with `/path/to/rust-clippy` + directory. 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Discord] channel.) @@ -185,6 +188,27 @@ Here is a TL;DR version of the sync process: ``` 5. Open a PR to [`rust-lang/rust`] +Also, you may want to define remotes, so you don't have to type out the remote +addresses on every sync. You can do this with the following commands (these +commands still have to be run inside the `rust` directory): + +```bash +# Set clippy-upstream remote for pulls +$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy +# Make sure to not push to the upstream repo +$ git remote set-url --push clippy-upstream DISABLED +# Set clippy-origin remote to your fork for pushes +$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy +# Set a local remote +$ git remote add clippy-local /path/to/rust-clippy +``` + +You can then sync with the remote names from above, e.g.: + +```bash +$ git subtree push -P src/tools/clippy clippy-local sync-from-rust +``` + [subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust From 07f1edf2d43efa0ef53e5b6c56c895bc9738ab94 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 25 Apr 2020 23:33:11 +0300 Subject: [PATCH 0078/1110] improve and generalize `option_and_then_some` lint - rename it to bind_instead_of_map --- CHANGELOG.md | 4 +- clippy_lints/src/lib.rs | 7 +- clippy_lints/src/loops.rs | 2 +- .../src/methods/bind_instead_of_map.rs | 309 ++++++++++++++++++ clippy_lints/src/methods/mod.rs | 100 ++---- clippy_lints/src/types.rs | 2 +- src/lintlist/mod.rs | 14 +- ...n_some.fixed => bind_instead_of_map.fixed} | 4 +- ...nd_then_some.rs => bind_instead_of_map.rs} | 2 +- ...some.stderr => bind_instead_of_map.stderr} | 18 +- tests/ui/bind_instead_of_map_multipart.rs | 61 ++++ tests/ui/bind_instead_of_map_multipart.stderr | 73 +++++ tests/ui/blocks_in_if_conditions.fixed | 4 +- tests/ui/blocks_in_if_conditions.rs | 4 +- tests/ui/option_map_or_none.fixed | 2 +- tests/ui/option_map_or_none.rs | 2 +- 16 files changed, 503 insertions(+), 105 deletions(-) create mode 100644 clippy_lints/src/methods/bind_instead_of_map.rs rename tests/ui/{option_and_then_some.fixed => bind_instead_of_map.fixed} (90%) rename tests/ui/{option_and_then_some.rs => bind_instead_of_map.rs} (94%) rename tests/ui/{option_and_then_some.stderr => bind_instead_of_map.stderr} (50%) create mode 100644 tests/ui/bind_instead_of_map_multipart.rs create mode 100644 tests/ui/bind_instead_of_map_multipart.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d05819a973a..7abefe65424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -315,7 +315,7 @@ Released 2019-11-07 * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535) * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511) * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394) - * [`option_and_then_some`] [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) + * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498) * Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348) * Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403) @@ -1273,6 +1273,7 @@ Released 2018-09-13 [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison @@ -1494,7 +1495,6 @@ Released 2018-09-13 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref -[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bda0d5c0458..ec198b684b6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -650,6 +650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &mem_replace::MEM_REPLACE_OPTION_WITH_NONE, &mem_replace::MEM_REPLACE_WITH_DEFAULT, &mem_replace::MEM_REPLACE_WITH_UNINIT, + &methods::BIND_INSTEAD_OF_MAP, &methods::CHARS_LAST_CMP, &methods::CHARS_NEXT_CMP, &methods::CLONE_DOUBLE_REF, @@ -676,7 +677,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, - &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, @@ -1291,6 +1291,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(&methods::BIND_INSTEAD_OF_MAP), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_DOUBLE_REF), @@ -1307,7 +1308,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), - LintId::of(&methods::OPTION_AND_THEN_SOME), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::OR_FUN_CALL), @@ -1559,10 +1559,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_SINGLE_BINDING), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(&methods::BIND_INSTEAD_OF_MAP), LintId::of(&methods::CLONE_ON_COPY), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::OPTION_AND_THEN_SOME), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), @@ -1784,6 +1784,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4a9c411d7c8..84e8a010738 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1462,7 +1462,7 @@ fn check_for_loop_over_map_kv<'a, 'tcx>( let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( diag, - "use the corresponding method".into(), + "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs new file mode 100644 index 00000000000..32e86637569 --- /dev/null +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -0,0 +1,309 @@ +use super::{contains_return, BIND_INSTEAD_OF_MAP}; +use crate::utils::{ + in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::Span; + +pub(crate) struct OptionAndThenSome; +impl BindInsteadOfMap for OptionAndThenSome { + const TYPE_NAME: &'static str = "Option"; + const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Some"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::OPTION_SOME; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultAndThenOk; +impl BindInsteadOfMap for ResultAndThenOk { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Ok"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_OK; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultOrElseErrInfo; +impl BindInsteadOfMap for ResultOrElseErrInfo { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "or_else"; + const BAD_VARIANT_NAME: &'static str = "Err"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_ERR; + + const GOOD_METHOD_NAME: &'static str = "map_err"; +} + +pub(crate) trait BindInsteadOfMap { + const TYPE_NAME: &'static str; + const TYPE_QPATH: &'static [&'static str]; + + const BAD_METHOD_NAME: &'static str; + const BAD_VARIANT_NAME: &'static str; + const BAD_VARIANT_QPATH: &'static [&'static str]; + + const GOOD_METHOD_NAME: &'static str; + + fn no_op_msg() -> String { + format!( + "using `{}.{}({})`, which is a no-op", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME + ) + } + + fn lint_msg() -> String { + format!( + "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME, + Self::GOOD_METHOD_NAME + ) + } + + fn lint_closure_autofixable( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + args: &[hir::Expr<'_>], + closure_expr: &hir::Expr<'_>, + closure_args_span: Span, + ) -> bool { + if_chain! { + if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; + if let hir::ExprKind::Path(ref qpath) = some_expr.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if some_args.len() == 1; + then { + let inner_expr = &some_args[0]; + + if contains_return(inner_expr) { + return false; + } + + let some_inner_snip = if inner_expr.span.from_expansion() { + snippet_with_macro_callsite(cx, inner_expr.span, "_") + } else { + snippet(cx, inner_expr.span, "_") + }; + + let closure_args_snip = snippet(cx, closure_args_span, ".."); + let option_snip = snippet(cx, args[0].span, ".."); + let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::lint_msg().as_ref(), + "try this", + note, + Applicability::MachineApplicable, + ); + true + } else { + false + } + } + } + + fn lint_closure(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + if_chain! { + if !in_macro(ret_expr.span); + if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; + if let hir::ExprKind::Path(ref qpath) = func_path.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if args.len() == 1; + if !contains_return(&args[0]); + then { + suggs.push((ret_expr.span, args[0].span.source_callsite())); + true + } else { + false + } + } + }); + + if can_sugg { + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, Self::lint_msg().as_ref(), |diag| { + multispan_sugg_with_applicability( + diag, + "try this", + Applicability::MachineApplicable, + std::iter::once((*method_calls(expr, 1).2.get(0).unwrap(), Self::GOOD_METHOD_NAME.into())).chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ), + ) + }); + } + } + + /// Lint use of `_.and_then(|x| Some(y))` for `Option`s + fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if !match_type(cx, cx.tables.expr_ty(&args[0]), Self::TYPE_QPATH) { + return; + } + + match args[1].kind { + hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + Self::lint_closure(cx, expr, closure_expr); + } + }, + // `_.and_then(Some)` case, which is no-op. + hir::ExprKind::Path(ref qpath) if match_qpath(qpath, Self::BAD_VARIANT_QPATH) => { + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::no_op_msg().as_ref(), + "use the expression directly", + snippet(cx, args[0].span, "..").into(), + Applicability::MachineApplicable, + ); + }, + _ => {}, + } + } +} + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &hir::Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } + } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_, '_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir hir::Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + hir::ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), + hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e6094edc5d7..626427c15ec 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,3 +1,4 @@ +mod bind_instead_of_map; mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; @@ -7,6 +8,7 @@ use std::borrow::Cow; use std::fmt; use std::iter; +use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -306,27 +308,34 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`. + /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or + /// `_.or_else(|x| Err(y))`. /// /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map(|x| y)`. + /// `_.map(|x| y)` or `_.map_err(|x| y)`. /// /// **Known problems:** None /// /// **Example:** /// /// ```rust - /// let x = Some("foo"); - /// let _ = x.and_then(|s| Some(s.len())); + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().and_then(|s| Some(s.len())); + /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); + /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); /// ``` /// /// The correct use would be: /// /// ```rust - /// let x = Some("foo"); - /// let _ = x.map(|s| s.len()); + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().map(|s| s.len()); + /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); + /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); /// ``` - pub OPTION_AND_THEN_SOME, + pub BIND_INSTEAD_OF_MAP, complexity, "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } @@ -1243,7 +1252,7 @@ declare_lint_pass!(Methods => [ MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, - OPTION_AND_THEN_SOME, + BIND_INSTEAD_OF_MAP, OR_FUN_CALL, EXPECT_FUN_CALL, CHARS_NEXT_CMP, @@ -1302,7 +1311,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), - ["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]), + ["and_then", ..] => { + bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + }, + ["or_else", ..] => { + bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2601,73 +2616,6 @@ fn lint_map_or_none<'a, 'tcx>( ); } -/// Lint use of `_.and_then(|x| Some(y))` for `Option`s -fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"; - const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op"; - - let ty = cx.tables.expr_ty(&args[0]); - if !is_type_diagnostic_item(cx, ty, sym!(option_type)) { - return; - } - - match args[1].kind { - hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); - if_chain! { - if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; - if let hir::ExprKind::Path(ref qpath) = some_expr.kind; - if match_qpath(qpath, &paths::OPTION_SOME); - if some_args.len() == 1; - then { - let inner_expr = &some_args[0]; - - if contains_return(inner_expr) { - return; - } - - let some_inner_snip = if inner_expr.span.from_expansion() { - snippet_with_macro_callsite(cx, inner_expr.span, "_") - } else { - snippet(cx, inner_expr.span, "_") - }; - - let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, args[0].span, ".."); - let note = format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip); - span_lint_and_sugg( - cx, - OPTION_AND_THEN_SOME, - expr.span, - LINT_MSG, - "try this", - note, - Applicability::MachineApplicable, - ); - } - } - }, - // `_.and_then(Some)` case, which is no-op. - hir::ExprKind::Path(ref qpath) => { - if match_qpath(qpath, &paths::OPTION_SOME) { - let option_snip = snippet(cx, args[0].span, ".."); - let note = format!("{}", option_snip); - span_lint_and_sugg( - cx, - OPTION_AND_THEN_SOME, - expr.span, - NO_OP_MSG, - "use the expression directly", - note, - Applicability::MachineApplicable, - ); - } - }, - _ => {}, - } -} - /// lint use of `filter().next()` for `Iterators` fn lint_filter_next<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f50adbc48ab..6ed9ff22e46 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2230,7 +2230,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher { ); if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor".into(), vis.suggestions); + multispan_sugg(diag, "...and use generic constructor", vis.suggestions); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e5e3bf453a0..5b4e2906b5f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -66,6 +66,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bit_mask", }, + Lint { + name: "bind_instead_of_map", + group: "complexity", + desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", + deprecation: None, + module: "methods", + }, Lint { name: "blacklisted_name", group: "style", @@ -1578,13 +1585,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "eq_op", }, - Lint { - name: "option_and_then_some", - group: "complexity", - desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", - deprecation: None, - module: "methods", - }, Lint { name: "option_as_ref_deref", group: "complexity", diff --git a/tests/ui/option_and_then_some.fixed b/tests/ui/bind_instead_of_map.fixed similarity index 90% rename from tests/ui/option_and_then_some.fixed rename to tests/ui/bind_instead_of_map.fixed index 035bc1e5465..5815550d7a6 100644 --- a/tests/ui/option_and_then_some.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![deny(clippy::option_and_then_some)] +#![deny(clippy::bind_instead_of_map)] // need a main anyway, use it get rid of unused warnings too pub fn main() { @@ -12,7 +12,7 @@ pub fn main() { // Different type let x: Result = Ok(1); - let _ = x.and_then(Ok); + let _ = x; } pub fn foo() -> Option { diff --git a/tests/ui/option_and_then_some.rs b/tests/ui/bind_instead_of_map.rs similarity index 94% rename from tests/ui/option_and_then_some.rs rename to tests/ui/bind_instead_of_map.rs index d49da7813c6..623b100a4ce 100644 --- a/tests/ui/option_and_then_some.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,5 @@ // run-rustfix -#![deny(clippy::option_and_then_some)] +#![deny(clippy::bind_instead_of_map)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/option_and_then_some.stderr b/tests/ui/bind_instead_of_map.stderr similarity index 50% rename from tests/ui/option_and_then_some.stderr rename to tests/ui/bind_instead_of_map.stderr index 47825491765..24c6b7f9ef3 100644 --- a/tests/ui/option_and_then_some.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,20 +1,26 @@ error: using `Option.and_then(Some)`, which is a no-op - --> $DIR/option_and_then_some.rs:8:13 + --> $DIR/bind_instead_of_map.rs:8:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` | note: the lint level is defined here - --> $DIR/option_and_then_some.rs:2:9 + --> $DIR/bind_instead_of_map.rs:2:9 | -LL | #![deny(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/option_and_then_some.rs:9:13 + --> $DIR/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` -error: aborting due to 2 previous errors +error: using `Result.and_then(Ok)`, which is a no-op + --> $DIR/bind_instead_of_map.rs:15:13 + | +LL | let _ = x.and_then(Ok); + | ^^^^^^^^^^^^^^ help: use the expression directly: `x` + +error: aborting due to 3 previous errors diff --git a/tests/ui/bind_instead_of_map_multipart.rs b/tests/ui/bind_instead_of_map_multipart.rs new file mode 100644 index 00000000000..91d9d11e3c1 --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.rs @@ -0,0 +1,61 @@ +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").and_then(|s| { + if { + if s == "43" { + return Some(43); + } + s == "42" + } { + return Some(45); + } + match s.len() { + 10 => Some(2), + 20 => { + if foo() { + return { + if foo() { + return Some(20); + } + println!("foo"); + Some(3) + }; + } + Some(20) + }, + 40 => Some(30), + _ => Some(1), + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); +} diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr new file mode 100644 index 00000000000..50ce2f4051e --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -0,0 +1,73 @@ +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:5:13 + | +LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bind_instead_of_map_multipart.rs:1:9 + | +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try this + | +LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:8:13 + | +LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:11:13 + | +LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + | ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:19:5 + | +LL | / Some("42").and_then(|s| { +LL | | if { +LL | | if s == "43" { +LL | | return Some(43); +... | +LL | | } +LL | | }); + | |______^ + | +help: try this + | +LL | Some("42").map(|s| { +LL | if { +LL | if s == "43" { +LL | return 43; +LL | } +LL | s == "42" + ... + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:60:13 + | +LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); + | ^^^ ^^^^ ^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/blocks_in_if_conditions.fixed b/tests/ui/blocks_in_if_conditions.fixed index 9040552cefc..14562c4d32c 100644 --- a/tests/ui/blocks_in_if_conditions.fixed +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -63,10 +63,10 @@ fn block_in_assert() { let opt = Some(42); assert!(opt .as_ref() - .and_then(|val| { + .map(|val| { let mut v = val * 2; v -= 1; - Some(v * 3) + v * 3 }) .is_some()); } diff --git a/tests/ui/blocks_in_if_conditions.rs b/tests/ui/blocks_in_if_conditions.rs index 2fe409b22d3..bda87650f6d 100644 --- a/tests/ui/blocks_in_if_conditions.rs +++ b/tests/ui/blocks_in_if_conditions.rs @@ -63,10 +63,10 @@ fn block_in_assert() { let opt = Some(42); assert!(opt .as_ref() - .and_then(|val| { + .map(|val| { let mut v = val * 2; v -= 1; - Some(v * 3) + v * 3 }) .is_some()); } diff --git a/tests/ui/option_map_or_none.fixed b/tests/ui/option_map_or_none.fixed index decbae4e5af..d80c3c7c1b7 100644 --- a/tests/ui/option_map_or_none.fixed +++ b/tests/ui/option_map_or_none.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::option_and_then_some)] +#![allow(clippy::bind_instead_of_map)] fn main() { let opt = Some(1); diff --git a/tests/ui/option_map_or_none.rs b/tests/ui/option_map_or_none.rs index 0f1d2218d5d..629842419e5 100644 --- a/tests/ui/option_map_or_none.rs +++ b/tests/ui/option_map_or_none.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::option_and_then_some)] +#![allow(clippy::bind_instead_of_map)] fn main() { let opt = Some(1); From 1b3dc5f79b98ba92c0b6fa98e62c8f331faa8ed6 Mon Sep 17 00:00:00 2001 From: Rahul Butani Date: Sun, 17 May 2020 22:21:02 -0500 Subject: [PATCH 0079/1110] Add to the list of words clippy::doc_markdown ignores --- clippy_lints/src/utils/conf.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 57b9eafd14d..9e8e0ff30ec 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -120,10 +120,12 @@ define_Conf! { "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", - "JavaScript", + "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", - "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap", + "OCaml", + "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", + "TensorFlow", "TrueType", "iOS", "macOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", From 842dd072612a5a53bef37f242f8f2be8896902cc Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 19 May 2020 15:18:16 +0200 Subject: [PATCH 0080/1110] Add note that a subtree fix and stack limit increase is required --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6697ff2f40d..c6a3998ec4e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,6 +209,13 @@ You can then sync with the remote names from above, e.g.: $ git subtree push -P src/tools/clippy clippy-local sync-from-rust ``` +_Note:_ The first time running `git subtree push` a cache has to be built. This +involves going through the complete Clippy history once. For this you have to +increase the stack limit though, which you can do with `ulimit -s 60000`. For +this to work, you will need the fix of `git subtree` available +[here][gitgitgadget-pr]. + +[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 [subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust From 1a9ba3b2c208f8131587ece3ad8fb159336dd694 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 19 May 2020 16:36:14 +0200 Subject: [PATCH 0081/1110] Add note, that a merge commit after push is necessary --- CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6a3998ec4e..c9180e58fc2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -178,6 +178,15 @@ to be run inside the `rust` directory): _Note:_ This will directly push to the remote repository. You can also push to your local copy by replacing the remote address with `/path/to/rust-clippy` directory. + + _Note:_ Most of the time you have to create a merge commit in the + `rust-clippy` repo (this has to be done in the Clippy repo, not in the + rust-copy of Clippy): + ```bash + git checkout sync-from-rust + git fetch upstream + git merge upstream/master + ``` 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Discord] channel.) From da9b138ec7645d483112c6b20e91ab595326c41d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 19 May 2020 16:12:03 +0200 Subject: [PATCH 0082/1110] Update test after const_ptr functions are must_use now --- tests/ui/ptr_offset_with_cast.fixed | 12 ++++++------ tests/ui/ptr_offset_with_cast.rs | 12 ++++++------ tests/ui/ptr_offset_with_cast.stderr | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index ebdd6c4003d..718e391e8bf 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -9,12 +9,12 @@ fn main() { let offset_isize = 1_isize; unsafe { - ptr.add(offset_usize); - ptr.offset(offset_isize as isize); - ptr.offset(offset_u8 as isize); + let _ = ptr.add(offset_usize); + let _ = ptr.offset(offset_isize as isize); + let _ = ptr.offset(offset_u8 as isize); - ptr.wrapping_add(offset_usize); - ptr.wrapping_offset(offset_isize as isize); - ptr.wrapping_offset(offset_u8 as isize); + let _ = ptr.wrapping_add(offset_usize); + let _ = ptr.wrapping_offset(offset_isize as isize); + let _ = ptr.wrapping_offset(offset_u8 as isize); } } diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index 3416c4b727a..f613742c741 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -9,12 +9,12 @@ fn main() { let offset_isize = 1_isize; unsafe { - ptr.offset(offset_usize as isize); - ptr.offset(offset_isize as isize); - ptr.offset(offset_u8 as isize); + let _ = ptr.offset(offset_usize as isize); + let _ = ptr.offset(offset_isize as isize); + let _ = ptr.offset(offset_u8 as isize); - ptr.wrapping_offset(offset_usize as isize); - ptr.wrapping_offset(offset_isize as isize); - ptr.wrapping_offset(offset_u8 as isize); + let _ = ptr.wrapping_offset(offset_usize as isize); + let _ = ptr.wrapping_offset(offset_isize as isize); + let _ = ptr.wrapping_offset(offset_u8 as isize); } } diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index b5c7a03e277..fd45224ca06 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -1,16 +1,16 @@ error: use of `offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:12:9 + --> $DIR/ptr_offset_with_cast.rs:12:17 | -LL | ptr.offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` +LL | let _ = ptr.offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` | = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` error: use of `wrapping_offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:16:9 + --> $DIR/ptr_offset_with_cast.rs:16:17 | -LL | ptr.wrapping_offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` +LL | let _ = ptr.wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` error: aborting due to 2 previous errors From f28f1f15da825bcf5cf78413f464dfea0bc553e5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 20 May 2020 13:32:53 +0200 Subject: [PATCH 0083/1110] Fix dogfood fallout --- clippy_lints/src/utils/inspector.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 748c11fac64..9b672b9ec22 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -289,21 +289,21 @@ fn print_expr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}operands:", ind); for op in asm.operands { match op { - hir::InlineAsmOperand::In { expr, .. } => print_expr(cx, expr, indent + 1), + hir::InlineAsmOperand::In { expr, .. } + | hir::InlineAsmOperand::InOut { expr, .. } + | hir::InlineAsmOperand::Const { expr } + | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { print_expr(cx, expr, indent + 1); } }, - hir::InlineAsmOperand::InOut { expr, .. } => print_expr(cx, expr, indent + 1), hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { print_expr(cx, in_expr, indent + 1); if let Some(out_expr) = out_expr { print_expr(cx, out_expr, indent + 1); } }, - hir::InlineAsmOperand::Const { expr } => print_expr(cx, expr, indent + 1), - hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), } } }, From ecd0a67b01e13d7a80d2f64bbfa5da1e568367e5 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 20 May 2020 13:23:51 +0300 Subject: [PATCH 0084/1110] Make match_wild_err_arm pedantic, and update help messages --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/matches.rs | 6 +++--- src/lintlist/mod.rs | 2 +- tests/ui/match_wild_err_arm.stderr | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4d4fff883b3..057d39d4c82 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1141,6 +1141,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), + LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), @@ -1285,7 +1286,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_SINGLE_BINDING), - LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), @@ -1476,7 +1476,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), - LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH), LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4106e5013b9..94380acfcfd 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -168,7 +168,7 @@ declare_clippy_lint! { /// **What it does:** Checks for arm which matches all errors with `Err(_)` /// and take drastic actions like `panic!`. /// - /// **Why is this bad?** It is generally a bad practice, just like + /// **Why is this bad?** It is generally a bad practice, similar to /// catching all exceptions in java with `catch(Exception)` /// /// **Known problems:** None. @@ -182,7 +182,7 @@ declare_clippy_lint! { /// } /// ``` pub MATCH_WILD_ERR_ARM, - style, + pedantic, "a `match` with `Err(_)` arm and take drastic actions" } @@ -711,7 +711,7 @@ fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) arm.pat.span, &format!("`Err({})` matches all errors", &ident_bind_name), None, - "match each error separately or use the error output", + "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable", ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0bf46491d31..8211a57b564 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1195,7 +1195,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "match_wild_err_arm", - group: "style", + group: "pedantic", desc: "a `match` with `Err(_)` arm and take drastic actions", deprecation: None, module: "matches", diff --git a/tests/ui/match_wild_err_arm.stderr b/tests/ui/match_wild_err_arm.stderr index 20d4c933418..6a2a02987de 100644 --- a/tests/ui/match_wild_err_arm.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -5,7 +5,7 @@ LL | Err(_) => panic!("err"), | ^^^^^^ | = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_)` matches all errors --> $DIR/match_wild_err_arm.rs:17:9 @@ -13,7 +13,7 @@ error: `Err(_)` matches all errors LL | Err(_) => panic!(), | ^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_)` matches all errors --> $DIR/match_wild_err_arm.rs:23:9 @@ -21,7 +21,7 @@ error: `Err(_)` matches all errors LL | Err(_) => { | ^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_e)` matches all errors --> $DIR/match_wild_err_arm.rs:31:9 @@ -29,7 +29,7 @@ error: `Err(_e)` matches all errors LL | Err(_e) => panic!(), | ^^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: aborting due to 4 previous errors From 2db7f1abf84699605a5863887484cbf587db3eb1 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 20 May 2020 16:46:30 +0300 Subject: [PATCH 0085/1110] Update future-not-send stderr output --- tests/ui/future_not_send.stderr | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index d1863701bfe..b59dbb3e76c 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -47,17 +47,32 @@ error: future cannot be sent between threads safely --> $DIR/future_not_send.rs:20:63 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^ + | ^^^^ future returned by `private_future2` is not `Send` | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:20:26 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` +note: captured value is not `Send` + --> $DIR/future_not_send.rs:20:40 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ has type `&std::cell::Cell` which is not `Send` = note: `std::cell::Cell` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely --> $DIR/future_not_send.rs:24:43 | LL | pub async fn public_future2(rc: Rc<[u8]>) {} - | ^ + | ^ future returned by `public_future2` is not `Send` | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:24:29 + | +LL | pub async fn public_future2(rc: Rc<[u8]>) {} + | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely @@ -117,8 +132,13 @@ error: future cannot be sent between threads safely --> $DIR/future_not_send.rs:66:34 | LL | async fn unclear_future(t: T) {} - | ^ + | ^ future returned by `unclear_future` is not `Send` | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:66:28 + | +LL | async fn unclear_future(t: T) {} + | ^ has type `T` which is not `Send` = note: `T` doesn't implement `std::marker::Send` error: aborting due to 8 previous errors From bd9b09e29396697874f63d82926b36fa154caa1f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:54:09 +0200 Subject: [PATCH 0086/1110] Adapt compile-test to run tests for cargo lints --- tests/compile-test.rs | 150 ++++++++++++++++++------ tests/ui-cargo/update-all-references.sh | 18 +++ tests/ui-cargo/update-references.sh | 38 ++++++ 3 files changed, 169 insertions(+), 37 deletions(-) create mode 100755 tests/ui-cargo/update-all-references.sh create mode 100755 tests/ui-cargo/update-references.sh diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a3df9d5ccbd..91b9c73c9d4 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -101,49 +101,124 @@ fn run_mode(cfg: &mut compiletest::Config) { compiletest::run_tests(&cfg); } -#[allow(clippy::identity_conversion)] -fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec) -> Result { - let mut result = true; - let opts = compiletest::test_opts(config); - for dir in fs::read_dir(&config.src_base)? { - let dir = dir?; - if !dir.file_type()?.is_dir() { - continue; - } - let dir_path = dir.path(); - set_var("CARGO_MANIFEST_DIR", &dir_path); - for file in fs::read_dir(&dir_path)? { - let file = file?; - let file_path = file.path(); - if file.file_type()?.is_dir() { - continue; - } - if file_path.extension() != Some(OsStr::new("rs")) { - continue; - } - let paths = compiletest::common::TestPaths { - file: file_path, - base: config.src_base.clone(), - relative_dir: dir_path.file_name().unwrap().into(), - }; - let test_name = compiletest::make_test_name(&config, &paths); - let index = tests - .iter() - .position(|test| test.desc.name == test_name) - .expect("The test should be in there"); - result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; - } - } - Ok(result) -} - fn run_ui_toml(config: &mut compiletest::Config) { + fn run_tests(config: &compiletest::Config, mut tests: Vec) -> Result { + let mut result = true; + let opts = compiletest::test_opts(config); + for dir in fs::read_dir(&config.src_base)? { + let dir = dir?; + if !dir.file_type()?.is_dir() { + continue; + } + let dir_path = dir.path(); + set_var("CARGO_MANIFEST_DIR", &dir_path); + for file in fs::read_dir(&dir_path)? { + let file = file?; + let file_path = file.path(); + if file.file_type()?.is_dir() { + continue; + } + if file_path.extension() != Some(OsStr::new("rs")) { + continue; + } + let paths = compiletest::common::TestPaths { + file: file_path, + base: config.src_base.clone(), + relative_dir: dir_path.file_name().unwrap().into(), + }; + let test_name = compiletest::make_test_name(&config, &paths); + let index = tests + .iter() + .position(|test| test.desc.name == test_name) + .expect("The test should be in there"); + result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; + } + } + Ok(result) + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap(); let tests = compiletest::make_tests(&config); - let res = run_ui_toml_tests(&config, tests); + let res = run_tests(&config, tests); + match res { + Ok(true) => {}, + Ok(false) => panic!("Some tests failed"), + Err(e) => { + println!("I/O failure during tests: {:?}", e); + }, + } +} + +fn run_ui_cargo(config: &mut compiletest::Config) { + fn run_tests( + config: &compiletest::Config, + filter: &Option, + mut tests: Vec, + ) -> Result { + let mut result = true; + let opts = compiletest::test_opts(config); + + for dir in fs::read_dir(&config.src_base)? { + let dir = dir?; + if !dir.file_type()?.is_dir() { + continue; + } + + // Use the filter if provided + let dir_path = dir.path(); + match &filter { + Some(name) if !dir_path.ends_with(name) => continue, + _ => {}, + } + + for case in &["pass", "fail"] { + let tail: PathBuf = [case, "src"].iter().collect(); + let src_path = dir_path.join(tail); + env::set_current_dir(&src_path)?; + + for file in fs::read_dir(&src_path)? { + let file = file?; + if file.file_type()?.is_dir() { + continue; + } + + // Search for the main file to avoid running a test for each file in the project + let file_path = file.path(); + match file_path.file_name().and_then(OsStr::to_str) { + Some("main.rs") => {}, + _ => continue, + } + + let paths = compiletest::common::TestPaths { + file: file_path, + base: config.src_base.clone(), + relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(), + }; + let test_name = compiletest::make_test_name(&config, &paths); + let index = tests + .iter() + .position(|test| test.desc.name == test_name) + .expect("The test should be in there"); + result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; + } + } + } + Ok(result) + } + + config.mode = TestMode::Ui; + config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); + + let tests = compiletest::make_tests(&config); + + let current_dir = env::current_dir().unwrap(); + let filter = env::var("TESTNAME").ok(); + let res = run_tests(&config, &filter, tests); + env::set_current_dir(current_dir).unwrap(); + match res { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), @@ -165,4 +240,5 @@ fn compile_test() { let mut config = default_config(); run_mode(&mut config); run_ui_toml(&mut config); + run_ui_cargo(&mut config); } diff --git a/tests/ui-cargo/update-all-references.sh b/tests/ui-cargo/update-all-references.sh new file mode 100755 index 00000000000..7028b251ea0 --- /dev/null +++ b/tests/ui-cargo/update-all-references.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# A script to update the references for all tests. The idea is that +# you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. You then +# run this script, which will copy those files over. If you find +# yourself manually editing a foo.stderr file, you're doing it wrong. +# +# See all `update-references.sh`, if you just want to update a single test. + +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + echo "usage: $0" +fi + +BUILD_DIR=$PWD/target/debug/test_build_base +MY_DIR=$(dirname "$0") +cd "$MY_DIR" || exit +find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh new file mode 100755 index 00000000000..50d42678734 --- /dev/null +++ b/tests/ui-cargo/update-references.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# A script to update the references for particular tests. The idea is +# that you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. This +# script will then copy that output and replace the "expected output" +# files. You can then commit the changes. +# +# If you find yourself manually editing a foo.stderr file, you're +# doing it wrong. + +if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then + echo "usage: $0 " + echo "" + echo "For example:" + echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" +fi + +MYDIR=$(dirname "$0") + +BUILD_DIR="$1" +shift + +while [[ "$1" != "" ]]; do + STDERR_NAME="${1/%.rs/.stderr}" + STDOUT_NAME="${1/%.rs/.stdout}" + shift + if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then + echo updating "$MYDIR"/"$STDOUT_NAME" + cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + fi + if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then + echo updating "$MYDIR"/"$STDERR_NAME" + cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + fi +done From 96af3e83601a6cd37544e70a7a816c36cc9871f5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:55:12 +0200 Subject: [PATCH 0087/1110] Add test for wildcard_dependencies --- tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml | 6 ++++++ tests/ui-cargo/wildcard_dependencies/fail/src/main.rs | 3 +++ tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr | 6 ++++++ tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml | 6 ++++++ tests/ui-cargo/wildcard_dependencies/pass/src/main.rs | 3 +++ 5 files changed, 24 insertions(+) create mode 100644 tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml create mode 100644 tests/ui-cargo/wildcard_dependencies/fail/src/main.rs create mode 100644 tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr create mode 100644 tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml create mode 100644 tests/ui-cargo/wildcard_dependencies/pass/src/main.rs diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml new file mode 100644 index 00000000000..9558dd68091 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wildcard_dependencies" +version = "0.1.0" + +[dependencies] +regex = "*" diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs new file mode 100644 index 00000000000..3491ccb0d47 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::wildcard_dependencies)] + +fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr b/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr new file mode 100644 index 00000000000..9e65d2f9942 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr @@ -0,0 +1,6 @@ +error: wildcard dependency for `regex` + | + = note: `-D clippy::wildcard-dependencies` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml new file mode 100644 index 00000000000..062e441622a --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wildcard_dependencies" +version = "0.1.0" + +[dependencies] +regex = "1" diff --git a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs new file mode 100644 index 00000000000..3491ccb0d47 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::wildcard_dependencies)] + +fn main() {} From bc93f7052e4a76d62e2aa5f11649e662cb46c7ce Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:56:33 +0200 Subject: [PATCH 0088/1110] Add test for cargo_common_metadata Fix missing `authors` entry in the provided example --- clippy_lints/src/cargo_common_metadata.rs | 1 + .../cargo_common_metadata/fail/Cargo.toml | 3 +++ .../cargo_common_metadata/fail/src/main.rs | 3 +++ .../cargo_common_metadata/fail/src/main.stderr | 18 ++++++++++++++++++ .../cargo_common_metadata/pass/Cargo.toml | 10 ++++++++++ .../cargo_common_metadata/pass/src/main.rs | 3 +++ 6 files changed, 38 insertions(+) create mode 100644 tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml create mode 100644 tests/ui-cargo/cargo_common_metadata/fail/src/main.rs create mode 100644 tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr create mode 100644 tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml create mode 100644 tests/ui-cargo/cargo_common_metadata/pass/src/main.rs diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 782da249808..16b46423c8f 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -23,6 +23,7 @@ declare_clippy_lint! { /// [package] /// name = "clippy" /// version = "0.0.212" + /// authors = ["Someone "] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml new file mode 100644 index 00000000000..8346bf05778 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs new file mode 100644 index 00000000000..c67166fc4b0 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::cargo_common_metadata)] + +fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr new file mode 100644 index 00000000000..c8ae6c820df --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -0,0 +1,18 @@ +error: package `cargo_common_metadata` is missing `package.authors` metadata + | + = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + +error: package `cargo_common_metadata` is missing `package.description` metadata + +error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata + +error: package `cargo_common_metadata` is missing `package.repository` metadata + +error: package `cargo_common_metadata` is missing `package.readme` metadata + +error: package `cargo_common_metadata` is missing `package.keywords` metadata + +error: package `cargo_common_metadata` is missing `package.categories` metadata + +error: aborting due to 7 previous errors + diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml new file mode 100644 index 00000000000..f99c126fabf --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" +authors = ["Random person from the Internet "] +description = "A test package for the cargo_common_metadata lint" +repository = "https://github.com/someone/cargo_common_metadata" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["metadata", "lint", "clippy"] +categories = ["development-tools::testing"] diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs new file mode 100644 index 00000000000..c67166fc4b0 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::cargo_common_metadata)] + +fn main() {} From 7a0eccbd8a719af00b027b0ea85c576d9cbed750 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:58:02 +0200 Subject: [PATCH 0089/1110] Add test for multiple_crate_versions Make the output of the lint deterministic by sorting the versions --- clippy_lints/src/multiple_crate_versions.rs | 4 +++- tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml | 7 +++++++ tests/ui-cargo/multiple_crate_versions/fail/src/main.rs | 3 +++ .../ui-cargo/multiple_crate_versions/fail/src/main.stderr | 6 ++++++ tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml | 7 +++++++ tests/ui-cargo/multiple_crate_versions/pass/src/main.rs | 3 +++ 6 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr create mode 100644 tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/pass/src/main.rs diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index ed85d0315bd..c4decfc9401 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -54,7 +54,9 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { let group: Vec = group.collect(); if group.len() > 1 { - let versions = group.into_iter().map(|p| p.version).join(", "); + let mut versions: Vec<_> = group.into_iter().map(|p| p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); span_lint( cx, diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml new file mode 100644 index 00000000000..05ffde839dc --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "multiple_crate_versions" +version = "0.1.0" + +[dependencies] +ctrlc = "=3.1.0" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs new file mode 100644 index 00000000000..4bc61dd6299 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr new file mode 100644 index 00000000000..4f668599be9 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr @@ -0,0 +1,6 @@ +error: multiple versions for dependency `winapi`: 0.2.8, 0.3.8 + | + = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml new file mode 100644 index 00000000000..cad32b9233f --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" + +[dependencies] +regex = "1.3.7" +serde = "1.0.110" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs new file mode 100644 index 00000000000..4bc61dd6299 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} From 1eb6adf47579e3c56b38ba6cce7676e8d2d5beb0 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 21:03:37 +0200 Subject: [PATCH 0090/1110] Adapt `cargo dev new_lint` to create tests for cargo lints --- clippy_dev/src/new_lint.rs | 182 ++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 75 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 44b2a5383d2..843beaf3238 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,91 +1,110 @@ use crate::clippy_project_root; -use std::fs::{File, OpenOptions}; -use std::io; +use std::fs::{self, OpenOptions}; use std::io::prelude::*; -use std::io::ErrorKind; -use std::path::Path; +use std::io::{self, ErrorKind}; +use std::path::{Path, PathBuf}; -/// Creates files required to implement and test a new lint and runs `update_lints`. -/// -/// # Errors -/// -/// This function errors, if the files couldn't be created -pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> { - let pass = pass.expect("`pass` argument is validated by clap"); - let lint_name = lint_name.expect("`name` argument is validated by clap"); - let category = category.expect("`category` argument is validated by clap"); +struct LintData<'a> { + pass: &'a str, + name: &'a str, + category: &'a str, + project_root: PathBuf, +} - match open_files(lint_name) { - Ok((mut test_file, mut lint_file)) => { - let (pass_type, pass_lifetimes, pass_import, context_import) = match pass { - "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), - "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), - _ => { - unreachable!("`pass_type` should only ever be `early` or `late`!"); - }, - }; +trait Context { + fn context>(self, text: C) -> Self; +} - let camel_case_name = to_camel_case(lint_name); - - if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) { - return Err(io::Error::new( - ErrorKind::Other, - format!("Could not write to test file: {}", e), - )); - }; - - if let Err(e) = lint_file.write_all( - get_lint_file_contents( - pass_type, - pass_lifetimes, - lint_name, - &camel_case_name, - category, - pass_import, - context_import, - ) - .as_bytes(), - ) { - return Err(io::Error::new( - ErrorKind::Other, - format!("Could not write to lint file: {}", e), - )); - } - Ok(()) - }, - Err(e) => Err(io::Error::new( - ErrorKind::Other, - format!("Unable to create lint: {}", e), - )), +impl Context for io::Result { + fn context>(self, text: C) -> Self { + match self { + Err(e) => { + let message = format!("{}: {}", text.as_ref(), e); + Err(io::Error::new(ErrorKind::Other, message)) + }, + ok => ok, + } } } -fn open_files(lint_name: &str) -> Result<(File, File), io::Error> { - let project_root = clippy_project_root(); +/// Creates the files required to implement and test a new lint and runs `update_lints`. +/// +/// # Errors +/// +/// This function errors out if the files couldn't be created or written to. +pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> { + let lint = LintData { + pass: pass.expect("`pass` argument is validated by clap"), + name: lint_name.expect("`name` argument is validated by clap"), + category: category.expect("`category` argument is validated by clap"), + project_root: clippy_project_root(), + }; - let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name)); - let lint_file_path = project_root - .join("clippy_lints") - .join("src") - .join(format!("{}.rs", lint_name)); + create_lint(&lint).context("Unable to create lint implementation")?; + create_test(&lint).context("Unable to create a test for the new lint") +} - if Path::new(&test_file_path).exists() { - return Err(io::Error::new( - ErrorKind::AlreadyExists, - format!("test file {:?} already exists", test_file_path), - )); - } - if Path::new(&lint_file_path).exists() { - return Err(io::Error::new( - ErrorKind::AlreadyExists, - format!("lint file {:?} already exists", lint_file_path), - )); +fn create_lint(lint: &LintData) -> io::Result<()> { + let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { + "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), + "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), + _ => { + unreachable!("`pass_type` should only ever be `early` or `late`!"); + }, + }; + + let camel_case_name = to_camel_case(lint.name); + let lint_contents = get_lint_file_contents( + pass_type, + pass_lifetimes, + lint.name, + &camel_case_name, + lint.category, + pass_import, + context_import, + ); + + let lint_path = format!("clippy_lints/src/{}.rs", lint.name); + write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) +} + +fn create_test(lint: &LintData) -> io::Result<()> { + fn create_project_layout>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> { + let mut path = location.into().join(case); + fs::create_dir(&path)?; + write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?; + + path.push("src"); + fs::create_dir(&path)?; + write_file(path.join("main.rs"), get_test_file_contents(lint_name))?; + + Ok(()) } - let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?; - let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?; + if lint.category == "cargo" { + let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); + let test_dir = lint.project_root.join(relative_test_dir); + fs::create_dir(&test_dir)?; - Ok((test_file, lint_file)) + create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; + create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") + } else { + let test_path = format!("tests/ui/{}.rs", lint.name); + let test_contents = get_test_file_contents(lint.name); + write_file(lint.project_root.join(test_path), test_contents) + } +} + +fn write_file, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { + OpenOptions::new() + .write(true) + .create_new(true) + .open(path)? + .write_all(contents) + } + + inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display())) } fn to_camel_case(name: &str) -> String { @@ -112,6 +131,19 @@ fn main() {{ ) } +fn get_manifest_contents(lint_name: &str, hint: &str) -> String { + format!( + r#" +# {} + +[package] +name = "{}" +version = "0.1.0" +"#, + hint, lint_name + ) +} + fn get_lint_file_contents( pass_type: &str, pass_lifetimes: &str, From 5d0135e222448e637ec1d66b3dd5c0805884dedd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 21:39:56 +0200 Subject: [PATCH 0091/1110] Add documentation for testing cargo lints --- doc/adding_lints.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 9ad1315c175..75768681db9 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -42,8 +42,10 @@ case), and we don't need type information so it will have an early pass type `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` (category will default to nursery if not provided). This command will create two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`, -as well as run `cargo dev update_lints` to register the new lint. Next, we'll -open up these files and add our lint! +as well as run `cargo dev update_lints` to register the new lint. For cargo lints, +two project hierarchies (fail/pass) will be created under `tests/ui-cargo`. + +Next, we'll open up these files and add our lint! ## Testing @@ -105,6 +107,19 @@ our lint, we need to commit the generated `.stderr` files, too. In general, you should only commit files changed by `tests/ui/update-all-references.sh` for the specific lint you are creating/editing. +### Cargo lints + +For cargo lints, the process of testing differs in that we are interested in +the contents of the `Cargo.toml` files. If our new lint is named e.g. `foo_categories`, +after running `cargo dev new_lint` we will find two new manifest files: + +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. + +The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` +variable to `cargo uitest` works too, but the script to update the references +is in another path: `tests/ui-cargo/update-all-references.sh`. + ## Rustfix tests If the lint you are working on is making use of structured suggestions, the From 7ff71199df911b462800cf6bda7ac32879ba7eb1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 21 May 2020 14:46:04 +0200 Subject: [PATCH 0092/1110] Address comments from PR review --- clippy_dev/src/new_lint.rs | 1 + tests/compile-test.rs | 4 ++-- tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml | 1 + tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 1 + tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml | 1 + tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml | 1 + tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml | 1 + tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml | 1 + 8 files changed, 9 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 843beaf3238..80713ab569f 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -139,6 +139,7 @@ fn get_manifest_contents(lint_name: &str, hint: &str) -> String { [package] name = "{}" version = "0.1.0" +publish = false "#, hint, lint_name ) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 91b9c73c9d4..232b966f69a 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -147,7 +147,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - println!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {:?}", e); }, } } @@ -223,7 +223,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - println!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {:?}", e); }, } } diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml index 8346bf05778..c64adcf7c01 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -1,3 +1,4 @@ [package] name = "cargo_common_metadata" version = "0.1.0" +publish = false diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index f99c126fabf..c8233f328bb 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "cargo_common_metadata" version = "0.1.0" +publish = false authors = ["Random person from the Internet "] description = "A test package for the cargo_common_metadata lint" repository = "https://github.com/someone/cargo_common_metadata" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml index 05ffde839dc..3a94b723f3f 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "multiple_crate_versions" version = "0.1.0" +publish = false [dependencies] ctrlc = "=3.1.0" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml index cad32b9233f..a9b06420b33 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "cargo_common_metadata" version = "0.1.0" +publish = false [dependencies] regex = "1.3.7" diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml index 9558dd68091..fd2a3414856 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "wildcard_dependencies" version = "0.1.0" +publish = false [dependencies] regex = "*" diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml index 062e441622a..38cb139146e 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "wildcard_dependencies" version = "0.1.0" +publish = false [dependencies] regex = "1" From 1a04686fc0d2752de8731c833ab67bfae6136720 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 21 May 2020 14:47:13 +0200 Subject: [PATCH 0093/1110] Avoid triggering match_wildcard_for_single_variants --- clippy_dev/src/new_lint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 80713ab569f..08a2e0c0918 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -18,11 +18,11 @@ trait Context { impl Context for io::Result { fn context>(self, text: C) -> Self { match self { + Ok(t) => Ok(t), Err(e) => { let message = format!("{}: {}", text.as_ref(), e); Err(io::Error::new(ErrorKind::Other, message)) }, - ok => ok, } } } From f9013ff197a693798f0532f88bab0ae591d5ff82 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 21 May 2020 15:34:48 +0200 Subject: [PATCH 0094/1110] Relax fs layout so that multiple pass/fail manifests are possible --- doc/adding_lints.md | 11 ++++++++--- tests/compile-test.rs | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 75768681db9..b3f5a62d553 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -43,7 +43,7 @@ case), and we don't need type information so it will have an early pass type (category will default to nursery if not provided). This command will create two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to register the new lint. For cargo lints, -two project hierarchies (fail/pass) will be created under `tests/ui-cargo`. +two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! @@ -110,12 +110,17 @@ specific lint you are creating/editing. ### Cargo lints For cargo lints, the process of testing differs in that we are interested in -the contents of the `Cargo.toml` files. If our new lint is named e.g. `foo_categories`, -after running `cargo dev new_lint` we will find two new manifest files: +the `Cargo.toml` manifest file. We also need a minimal crate associated +with that manifest. + +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` +we will find by default two new crates, each with its manifest file: * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. * `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. +If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. + The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` variable to `cargo uitest` works too, but the script to update the references is in another path: `tests/ui-cargo/update-all-references.sh`. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 232b966f69a..a5de8429390 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -174,9 +174,13 @@ fn run_ui_cargo(config: &mut compiletest::Config) { _ => {}, } - for case in &["pass", "fail"] { - let tail: PathBuf = [case, "src"].iter().collect(); - let src_path = dir_path.join(tail); + for case in fs::read_dir(&dir_path)? { + let case = case?; + if !case.file_type()?.is_dir() { + continue; + } + + let src_path = case.path().join("src"); env::set_current_dir(&src_path)?; for file in fs::read_dir(&src_path)? { From c00268d984b80e408f56b5d8180e2f1a80100c91 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 22 May 2020 02:40:39 +0200 Subject: [PATCH 0095/1110] Also install llvm-tools on toolchain setup --- setup-toolchain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-toolchain.sh b/setup-toolchain.sh index 6038ed697f9..191ea4315a6 100755 --- a/setup-toolchain.sh +++ b/setup-toolchain.sh @@ -32,5 +32,5 @@ else TOOLCHAIN=() fi -rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -- "$RUST_COMMIT" +rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -c llvm-tools -- "$RUST_COMMIT" rustup override set master From 6b3cf63bf568cab4f8e05ea483ad97d5ea0e2eec Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 22 May 2020 14:45:51 +0200 Subject: [PATCH 0096/1110] Fix dogfood fallout --- clippy_lints/src/methods/mod.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 810a226b50d..32b3b7f7947 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1496,17 +1496,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { if let ty::Opaque(def_id, _) = ret_ty.kind { // one of the associated types must be Self for predicate in cx.tcx.predicates_of(def_id).predicates { - match predicate.0.kind() { - ty::PredicateKind::Projection(poly_projection_predicate) => { - let binder = poly_projection_predicate.ty(); - let associated_type = binder.skip_binder(); + if let ty::PredicateKind::Projection(poly_projection_predicate) = predicate.0.kind() { + let binder = poly_projection_predicate.ty(); + let associated_type = binder.skip_binder(); - // walk the associated type and check for Self - if contains_self_ty(associated_type) { - return; - } - }, - _ => {}, + // walk the associated type and check for Self + if contains_self_ty(associated_type) { + return; + } } } } From a578bed69ac2a9b33fcb871f9ad7dbf02355cb82 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 18 May 2020 18:35:49 -0400 Subject: [PATCH 0097/1110] new_without_default: do not suggest deriving --- clippy_lints/src/new_without_default.rs | 118 +++++------------------- tests/ui/new_without_default.rs | 11 +++ tests/ui/new_without_default.stderr | 35 ++++++- 3 files changed, 64 insertions(+), 100 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index a599667b8d8..3b88e4c4cb1 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,27 +1,20 @@ use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then}; +use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::HirIdSet; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for types with a `fn new() -> Self` method and no /// implementation of /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html). /// - /// It detects both the case when a manual - /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) - /// implementation is required and also when it can be created with - /// `#[derive(Default)]` - /// /// **Why is this bad?** The user might expect to be able to use /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the /// type can be constructed without arguments. @@ -40,46 +33,17 @@ declare_clippy_lint! { /// } /// ``` /// - /// Instead, use: + /// To fix the lint, and a `Default` implementation that delegates to `new`: /// /// ```ignore /// struct Foo(Bar); /// /// impl Default for Foo { /// fn default() -> Self { - /// Foo(Bar::new()) + /// Foo::new() /// } /// } /// ``` - /// - /// Or, if - /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) - /// can be derived by `#[derive(Default)]`: - /// - /// ```rust - /// struct Foo; - /// - /// impl Foo { - /// fn new() -> Self { - /// Foo - /// } - /// } - /// ``` - /// - /// Instead, use: - /// - /// ```rust - /// #[derive(Default)] - /// struct Foo; - /// - /// impl Foo { - /// fn new() -> Self { - /// Foo - /// } - /// } - /// ``` - /// - /// You can also have `new()` call `Default::default()`. pub NEW_WITHOUT_DEFAULT, style, "`fn new() -> Self` method without `Default` implementation" @@ -158,46 +122,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { } } - if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) { - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider deriving a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_item_with_attr( - cx, - sp, - "try this", - "#[derive(Default)]", - Applicability::MaybeIncorrect, - ); - }); - } else { - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try this", - &create_new_without_default_suggest_msg(self_ty), - Applicability::MaybeIncorrect, - ); - }, - ); - } + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id, + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{}`", + self_ty + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try this", + &create_new_without_default_suggest_msg(self_ty), + Applicability::MaybeIncorrect, + ); + }, + ); } } } @@ -217,18 +160,3 @@ fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { }} }}", ty) } - -fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option { - match ty.kind { - ty::Adt(adt_def, substs) if adt_def.is_struct() => { - for field in adt_def.all_fields() { - let f_ty = field.ty(cx.tcx, substs); - if !implements_trait(cx, f_ty, default_trait_id, &[]) { - return None; - } - } - Some(cx.tcx.def_span(adt_def.did)) - }, - _ => None, - } -} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 781ea7bb152..3b6041823d8 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -148,4 +148,15 @@ impl AllowDerive { } } +pub struct NewNotEqualToDerive { + foo: i32, +} + +impl NewNotEqualToDerive { + // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving. + pub fn new() -> Self { + NewNotEqualToDerive { foo: 1 } + } +} + fn main() {} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 5e485d40663..e529e441eb7 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,4 +1,4 @@ -error: you should consider deriving a `Default` implementation for `Foo` +error: you should consider adding a `Default` implementation for `Foo` --> $DIR/new_without_default.rs:8:5 | LL | / pub fn new() -> Foo { @@ -9,10 +9,14 @@ LL | | } = note: `-D clippy::new-without-default` implied by `-D warnings` help: try this | -LL | #[derive(Default)] +LL | impl Default for Foo { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } | -error: you should consider deriving a `Default` implementation for `Bar` +error: you should consider adding a `Default` implementation for `Bar` --> $DIR/new_without_default.rs:16:5 | LL | / pub fn new() -> Self { @@ -22,7 +26,11 @@ LL | | } | help: try this | -LL | #[derive(Default)] +LL | impl Default for Bar { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } | error: you should consider adding a `Default` implementation for `LtKo<'c>` @@ -42,5 +50,22 @@ LL | } LL | } | -error: aborting due to 3 previous errors +error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` + --> $DIR/new_without_default.rs:157:5 + | +LL | / pub fn new() -> Self { +LL | | NewNotEqualToDerive { foo: 1 } +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for NewNotEqualToDerive { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 4 previous errors From 29d043683e6f70b22ae34596b4cb9ae07274c28b Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 22 May 2020 19:09:24 +0200 Subject: [PATCH 0098/1110] option_option test case #4298 --- tests/ui/option_option.rs | 25 +++++++++++++++++++++++++ tests/ui/option_option.stderr | 8 +++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 904c50e1403..a2617a13eca 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -60,3 +60,28 @@ fn main() { // The lint allows this let expr = Some(Some(true)); } + +extern crate serde; +mod issue_4298 { + use serde::{Deserialize, Deserializer, Serialize}; + use std::borrow::Cow; + + #[derive(Serialize, Deserialize)] + struct Foo<'a> { + #[serde(deserialize_with = "func")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(borrow)] + // FIXME: should not lint here + #[allow(clippy::option_option)] + foo: Option>>, + } + + #[allow(clippy::option_option)] + fn func<'a, D>(_: D) -> Result>>, D::Error> + where + D: Deserializer<'a>, + { + Ok(Some(Some(Cow::Borrowed("hi")))) + } +} diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 79db186d7ea..0cd4c96eb4f 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -58,5 +58,11 @@ error: consider using `Option` instead of `Option>` or a custom enu LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:77:14 + | +LL | foo: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors From a709559705db19785b29ce4a9044d7aebaefec31 Mon Sep 17 00:00:00 2001 From: Nick Torres Date: Sat, 23 May 2020 16:14:38 -0700 Subject: [PATCH 0099/1110] Clarify the documentation of the `unnecessary_mut_passed` lint --- clippy_lints/src/mut_reference.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index e5680482e5b..67a1ac78a67 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Detects giving a mutable reference to a function that only + /// **What it does:** Detects passing a mutable reference to a function that only /// requires an immutable reference. /// /// **Why is this bad?** The immutable reference rules out all other references From 7a83eafd44b57196a454d10628d1cce1bfd60bd2 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 May 2020 17:11:07 +0200 Subject: [PATCH 0100/1110] Also fetch origin before merging master into the rustup branch --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9180e58fc2..0f47ac98fd2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,8 +183,8 @@ to be run inside the `rust` directory): `rust-clippy` repo (this has to be done in the Clippy repo, not in the rust-copy of Clippy): ```bash + git fetch origin && git fetch upstream git checkout sync-from-rust - git fetch upstream git merge upstream/master ``` 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to From cff5cff2f3a6687dfaf12b92762e70545e0abefe Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 22 May 2020 22:30:28 +0200 Subject: [PATCH 0101/1110] Make the name of the crate available in cargo UI tests --- clippy_dev/src/new_lint.rs | 17 ++++++++++++----- .../cargo_common_metadata/fail/src/main.rs | 1 + .../cargo_common_metadata/pass/src/main.rs | 1 + .../multiple_crate_versions/fail/src/main.rs | 1 + .../multiple_crate_versions/pass/src/main.rs | 1 + .../wildcard_dependencies/fail/src/main.rs | 1 + .../wildcard_dependencies/pass/src/main.rs | 1 + 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 08a2e0c0918..c0b2dac2f60 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -76,7 +76,8 @@ fn create_test(lint: &LintData) -> io::Result<()> { path.push("src"); fs::create_dir(&path)?; - write_file(path.join("main.rs"), get_test_file_contents(lint_name))?; + let header = format!("// compile-flags: --crate-name={}", lint_name); + write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; Ok(()) } @@ -90,7 +91,7 @@ fn create_test(lint: &LintData) -> io::Result<()> { create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") } else { let test_path = format!("tests/ui/{}.rs", lint.name); - let test_contents = get_test_file_contents(lint.name); + let test_contents = get_test_file_contents(lint.name, None); write_file(lint.project_root.join(test_path), test_contents) } } @@ -119,8 +120,8 @@ fn to_camel_case(name: &str) -> String { .collect() } -fn get_test_file_contents(lint_name: &str) -> String { - format!( +fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { + let mut contents = format!( "#![warn(clippy::{})] fn main() {{ @@ -128,7 +129,13 @@ fn main() {{ }} ", lint_name - ) + ); + + if let Some(header) = header_commands { + contents = format!("{}\n{}", header, contents); + } + + contents } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs index c67166fc4b0..27841e18aa9 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs index c67166fc4b0..27841e18aa9 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs index 4bc61dd6299..1b2d3ec9459 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs index 4bc61dd6299..1b2d3ec9459 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs index 3491ccb0d47..581babfeacb 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=wildcard_dependencies #![warn(clippy::wildcard_dependencies)] fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs index 3491ccb0d47..581babfeacb 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs +++ b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=wildcard_dependencies #![warn(clippy::wildcard_dependencies)] fn main() {} From 8642fc97dd1a9b4f0291726c47ec97d15599d74d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 22 May 2020 22:39:19 +0200 Subject: [PATCH 0102/1110] multiple_crate_versions: skip dev and build deps --- clippy_lints/src/multiple_crate_versions.rs | 59 ++++++++++++++----- .../5041_allow_dev_build/Cargo.toml | 17 ++++++ .../5041_allow_dev_build/src/main.rs | 4 ++ 3 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index c4decfc9401..b24ec897ef5 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -1,11 +1,14 @@ //! lint on multiple versions of a crate being used use crate::utils::{run_lints, span_lint}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{Crate, CRATE_HIR_ID}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; +use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId}; +use if_chain::if_chain; use itertools::Itertools; declare_clippy_lint! { @@ -34,37 +37,65 @@ declare_clippy_lint! { declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]); impl LateLintPass<'_, '_> for MultipleCrateVersions { + #[allow(clippy::find_map)] fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() { + let metadata = if let Ok(metadata) = MetadataCommand::new().exec() { metadata } else { span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata"); - return; }; + let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str(); let mut packages = metadata.packages; packages.sort_by(|a, b| a.name.cmp(&b.name)); - for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) { - let group: Vec = group.collect(); + if_chain! { + if let Some(resolve) = &metadata.resolve; + if let Some(local_id) = packages.iter().find(|p| p.name == *local_name).map(|p| &p.id); + then { + for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + let group: Vec<&Package> = group.collect(); - if group.len() > 1 { - let mut versions: Vec<_> = group.into_iter().map(|p| p.version).collect(); - versions.sort(); - let versions = versions.iter().join(", "); + if group.len() <= 1 { + continue; + } - span_lint( - cx, - MULTIPLE_CRATE_VERSIONS, - DUMMY_SP, - &format!("multiple versions for dependency `{}`: {}", name, versions), - ); + if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { + let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); + + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{}`: {}", name, versions), + ); + } + } } } } } + +fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool { + fn depends_on(node: &Node, dep_id: &PackageId) -> bool { + node.deps.iter().any(|dep| { + dep.pkg == *dep_id + && dep + .dep_kinds + .iter() + .any(|info| matches!(info.kind, DependencyKind::Normal)) + }) + } + + nodes + .iter() + .filter(|node| depends_on(node, dep_id)) + .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id)) +} diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml new file mode 100644 index 00000000000..72731fbc75d --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml @@ -0,0 +1,17 @@ +# Should not lint for dev or build dependencies. See issue 5041. + +[package] +name = "multiple_crate_versions" +version = "0.1.0" +publish = false + +# One of the versions of winapi is only a dev dependency: allowed +[dependencies] +ctrlc = "=3.1.0" +[dev-dependencies] +ansi_term = "=0.11.0" + +# Both versions of winapi are a build dependency: allowed +[build-dependencies] +ctrlc = "=3.1.0" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs new file mode 100644 index 00000000000..1b2d3ec9459 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs @@ -0,0 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions +#![warn(clippy::multiple_crate_versions)] + +fn main() {} From ec0a00e53980619a6313ff4f01099a1aebcfd9e6 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 24 May 2020 21:17:54 +0200 Subject: [PATCH 0103/1110] Use find_map instead of find() + map() --- clippy_lints/src/multiple_crate_versions.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index b24ec897ef5..b6770804e18 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -37,7 +37,6 @@ declare_clippy_lint! { declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]); impl LateLintPass<'_, '_> for MultipleCrateVersions { - #[allow(clippy::find_map)] fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) { return; @@ -56,7 +55,9 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { if_chain! { if let Some(resolve) = &metadata.resolve; - if let Some(local_id) = packages.iter().find(|p| p.name == *local_name).map(|p| &p.id); + if let Some(local_id) = packages + .iter() + .find_map(|p| if p.name == *local_name { Some(&p.id) } else { None }); then { for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { let group: Vec<&Package> = group.collect(); From 4f8909fad986dda68a9dcd172eaa362b6fce105b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 May 2020 21:28:31 +0200 Subject: [PATCH 0104/1110] Extend `useless_conversion` lint with TryFrom --- clippy_lints/src/useless_conversion.rs | 47 ++++++++++++++++++++------ clippy_lints/src/utils/paths.rs | 1 + tests/ui/useless_conversion.stderr | 20 +++++------ tests/ui/useless_conversion_try.rs | 25 ++++++++++++++ tests/ui/useless_conversion_try.stderr | 39 +++++++++++++++++++++ 5 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 tests/ui/useless_conversion_try.rs create mode 100644 tests/ui/useless_conversion_try.stderr diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 95921518986..0b080d9be2c 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,13 +1,16 @@ use crate::utils::{ - match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, + is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, + span_lint_and_help, span_lint_and_sugg, }; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts + /// **What it does:** Checks for `Into`, `From`, `TryFrom`,`IntoIter` calls that useless converts /// to the same type as caller. /// /// **Why is this bad?** Redundant code. @@ -26,7 +29,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" } #[derive(Default)] @@ -68,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -84,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -94,11 +97,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { }, ExprKind::Call(ref path, ref args) => { - if let ExprKind::Path(ref qpath) = path.kind { - if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() { + if_chain! { + if args.len() == 1; + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id(); + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + + then { + if_chain! { + if match_def_path(cx, def_id, &paths::TRY_FROM); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().nth(0); + if same_tys(cx, a_type, b); + + then { + let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "Useless conversion to the same type", + None, + &hint, + ); + } + } + if match_def_path(cx, def_id, &paths::FROM_FROM) { - let a = cx.tables.expr_ty(e); - let b = cx.tables.expr_ty(&args[0]); if same_tys(cx, a, b) { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); let sugg_msg = @@ -107,7 +134,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index b3ad2ad9d99..e00d726282a 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -128,6 +128,7 @@ pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned" pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 7df3507edfd..0b2947f7d62 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,55 +10,55 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs new file mode 100644 index 00000000000..abf0c891b52 --- /dev/null +++ b/tests/ui/useless_conversion_try.rs @@ -0,0 +1,25 @@ +#![deny(clippy::useless_conversion)] + +use std::convert::TryFrom; + +fn test_generic(val: T) -> T { + T::try_from(val).unwrap() +} + +fn test_generic2 + Into, U: From>(val: T) { + let _ = U::try_from(val).unwrap(); +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + + let _: String = TryFrom::try_from("foo").unwrap(); + let _ = String::try_from("foo").unwrap(); + #[allow(clippy::useless_conversion)] + let _ = String::try_from("foo").unwrap(); + + let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + let _ = String::try_from("foo".to_string()).unwrap(); + let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); +} diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr new file mode 100644 index 00000000000..b3cb01fbe32 --- /dev/null +++ b/tests/ui/useless_conversion_try.stderr @@ -0,0 +1,39 @@ +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:6:5 + | +LL | T::try_from(val).unwrap() + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/useless_conversion_try.rs:1:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing `T::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:22:21 + | +LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `TryFrom::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:23:13 + | +LL | let _ = String::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:24:13 + | +LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: aborting due to 4 previous errors + From 705bfdcc467c0ddd7eb61d3adb24809b27bae891 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 22 May 2020 11:46:17 +0200 Subject: [PATCH 0105/1110] Extend `useless_conversion` lint with TryInto --- clippy_lints/src/useless_conversion.rs | 38 +++++++++++++++++++++----- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 2 +- tests/ui/useless_conversion_try.rs | 17 +++++++++--- tests/ui/useless_conversion_try.stderr | 38 +++++++++++++++++++++----- 5 files changed, 77 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 0b080d9be2c..1645c5777b2 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -10,8 +10,8 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for `Into`, `From`, `TryFrom`,`IntoIter` calls that useless converts - /// to the same type as caller. + /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls + /// that useless converts to the same type as caller. /// /// **Why is this bad?** Redundant code. /// @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" } #[derive(Default)] @@ -39,6 +39,7 @@ pub struct UselessConversion { impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); +#[allow(clippy::too_many_lines)] impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { @@ -66,7 +67,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { let b = cx.tables.expr_ty(&args[0]); if same_tys(cx, a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); - span_lint_and_sugg( cx, USELESS_CONVERSION, @@ -94,6 +94,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { ); } } + if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { + if_chain! { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().next(); + if same_tys(cx, a_type, b); + + then { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "Useless conversion to the same type", + None, + "consider removing `.try_into()`", + ); + } + } + } }, ExprKind::Call(ref path, ref args) => { @@ -109,7 +130,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; - if let Some(a_type) = substs.types().nth(0); + if let Some(a_type) = substs.types().next(); if same_tys(cx, a_type, b); then { @@ -125,8 +146,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { } } - if match_def_path(cx, def_id, &paths::FROM_FROM) { - if same_tys(cx, a, b) { + if_chain! { + if match_def_path(cx, def_id, &paths::FROM_FROM); + if same_tys(cx, a, b); + + then { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index e00d726282a..779da7e6bf2 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -131,6 +131,7 @@ pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; +pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8211a57b564..f63301c7db0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2421,7 +2421,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "useless_conversion", group: "complexity", - desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type", deprecation: None, module: "useless_conversion", }, diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index abf0c891b52..ab4f960edb7 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,12 +1,16 @@ #![deny(clippy::useless_conversion)] -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; fn test_generic(val: T) -> T { - T::try_from(val).unwrap() + let _ = T::try_from(val).unwrap(); + val.try_into().unwrap() } fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.try_into().unwrap(); + let _: U = val.try_into().unwrap(); let _ = U::try_from(val).unwrap(); } @@ -14,12 +18,17 @@ fn main() { test_generic(10i32); test_generic2::(10i32); + let _: String = "foo".try_into().unwrap(); let _: String = TryFrom::try_from("foo").unwrap(); let _ = String::try_from("foo").unwrap(); #[allow(clippy::useless_conversion)] - let _ = String::try_from("foo").unwrap(); - + { + let _ = String::try_from("foo").unwrap(); + let _: String = "foo".try_into().unwrap(); + } + let _: String = "foo".to_string().try_into().unwrap(); let _: String = TryFrom::try_from("foo".to_string()).unwrap(); let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + let _: String = format!("Hello {}", "world").try_into().unwrap(); } diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index b3cb01fbe32..5afb5dc45d3 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,8 +1,8 @@ error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:6:5 + --> $DIR/useless_conversion_try.rs:6:13 | -LL | T::try_from(val).unwrap() - | ^^^^^^^^^^^^^^^^ +LL | let _ = T::try_from(val).unwrap(); + | ^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/useless_conversion_try.rs:1:9 @@ -12,7 +12,23 @@ LL | #![deny(clippy::useless_conversion)] = help: consider removing `T::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:22:21 + --> $DIR/useless_conversion_try.rs:7:5 + | +LL | val.try_into().unwrap() + | ^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:29:21 + | +LL | let _: String = "foo".to_string().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:23:13 + --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,12 +44,20 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:24:13 + --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider removing `String::try_from()` -error: aborting due to 4 previous errors +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:33:21 + | +LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 7 previous errors From 827041252c709dee70756633a33a13a0bacbd3a9 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 23 May 2020 09:35:56 +0200 Subject: [PATCH 0106/1110] Add common lint tools doc --- doc/adding_lints.md | 1 + doc/common_tools_writing_lints.md | 152 ++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 doc/common_tools_writing_lints.md diff --git a/doc/adding_lints.md b/doc/adding_lints.md index b3f5a62d553..8092be277cc 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -465,6 +465,7 @@ Here are some pointers to things you are likely going to need for every lint: * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] +* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations * [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts * [The nightly rustc docs][nightly_docs] which has been linked to throughout this guide diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md new file mode 100644 index 00000000000..ed33b37c6bd --- /dev/null +++ b/doc/common_tools_writing_lints.md @@ -0,0 +1,152 @@ +# Common tools for writing lints + +You may need following tooltips to catch up with common operations. + +- [Common tools for writing lints](#common-tools-for-writing-lints) + - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression) + - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait) + - [Dealing with macros](#dealing-with-macros) + +Useful Rustc dev guide links: +- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) +- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) +- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) + +# Retrieving the type of an expression + +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: + +- which type does this expression correspond to (using its [`TyKind`][TyKind])? +- is it a sized type? +- is it a primitive type? +- does it implement a trait? + +This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckTables`][TypeckTables] struct, +that gives you access to the underlying structure [`TyS`][TyS]. + +Example of use: +```rust +impl LateLintPass<'_, '_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + // Get type of `expr` + let ty = cx.tables.expr_ty(expr); + // Match its kind to enter its type + match ty.kind { + ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"), + _ => () + } + } +} +``` + +Similarly in [`TypeckTables`][TypeckTables] methods, you have the [`pat_ty()`][pat_ty] method +to retrieve a type from a pattern. + +Two noticeable items here: +- `cx` is the lint context [`LateContext`][LateContext]. + The two most useful data structures in this context are `tcx` and `tables`, + allowing us to jump to type definitions and other compilation stages such as HIR. +- `tables` is [`TypeckTables`][TypeckTables] and is created by type checking step, + it includes useful information such as types of expressions, ways to resolve methods and so on. + +# Checking if a type implements a specific trait + +There are two ways to do this, depending if the target trait is part of lang items. + +```rust +use crate::utils::{implements_trait, match_trait_method, paths}; + +impl LateLintPass<'_, '_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + // 1. Using expression and Clippy's convenient method + // we use `match_trait_method` function from Clippy's toolbox + if match_trait_method(cx, expr, &paths::INTO) { + // `expr` implements `Into` trait + } + + // 2. Using type context `TyCtxt` + let ty = cx.tables.expr_ty(expr); + if cx.tcx.lang_items() + // we are looking for the `DefId` of `Drop` trait in lang items + .drop_trait() + // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils + .map_or(false, |id| implements_trait(cx, ty, id, &[])) { + // `expr` implements `Drop` trait + } + } +} +``` + +> Prefer using lang items, if the target trait is available there. + +A list of defined paths for Clippy can be found in [paths.rs][paths] + +We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. + +# Dealing with macros + +There are several helpers in Clippy's utils to deal with macros: + +- `in_macro()`: detect if the given span is expanded by a macro + +You may want to use this for example to not start linting in any macro. + +```rust +macro_rules! foo { + ($param:expr) => { + match $param { + "bar" => println!("whatever"), + _ => () + } + }; +} + +foo!("bar"); + +// if we lint the `match` of `foo` call and test its span +assert_eq!(in_macro(match_span), true); +``` + +- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate + +You may want to use it for example to not start linting in macros from other crates + +```rust +#[macro_use] +extern crate a_crate_with_macros; + +// `foo` is defined in `a_crate_with_macros` +foo!("bar"); + +// if we lint the `match` of `foo` call and test its span +assert_eq!(in_external_macro(cx.sess(), match_span), true); +``` + +- `differing_macro_contexts()`: returns true if the two given spans are not from the same context + +```rust +macro_rules! m { + ($a:expr, $b:expr) => { + if $a.is_some() { + $b; + } + } +} + +let x: Option = Some(42); +m!(x, x.unwrap()); + +// These spans are not from the same context +// x.is_some() is from inside the macro +// x.unwrap() is from outside the macro +assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true); +``` + +[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TypeckTables]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckTables.html +[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckTables.html#method.expr_ty +[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckTables.html#method.pat_ty +[paths]: ../clippy_lints/src/utils/paths.rs From 60d38ee1dde4344daa5fdf716eef78b45f483c7e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 23 May 2020 22:07:03 +0200 Subject: [PATCH 0107/1110] reversed_empty_ranges: add suggestion for &slice[N..N] --- clippy_lints/src/ranges.rs | 30 ++++++++++++++----- tests/ui/reversed_empty_ranges_fixable.fixed | 7 ++++- tests/ui/reversed_empty_ranges_fixable.rs | 7 ++++- tests/ui/reversed_empty_ranges_fixable.stderr | 16 ++++++---- tests/ui/reversed_empty_ranges_unfixable.rs | 1 - .../ui/reversed_empty_ranges_unfixable.stderr | 10 ++----- 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 83c6faac041..1eb26d97ed4 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -241,14 +241,14 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { - matches!( - get_parent_expr(cx, expr), - Some(Expr { + fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> { + match get_parent_expr(cx, expr) { + parent_expr @ Some(Expr { kind: ExprKind::Index(..), .. - }) - ) + }) => parent_expr, + _ => None, + } } fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { @@ -267,18 +267,32 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { - if inside_indexing_expr(cx, expr) { + if let Some(parent_expr) = inside_indexing_expr(cx, expr) { let (reason, outcome) = if ordering == Ordering::Equal { ("empty", "always yield an empty slice") } else { ("reversed", "panic at run-time") }; - span_lint( + span_lint_and_then( cx, REVERSED_EMPTY_RANGES, expr.span, &format!("this range is {} and using it to index a slice will {}", reason, outcome), + |diag| { + if_chain! { + if ordering == Ordering::Equal; + if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind; + then { + diag.span_suggestion( + parent_expr.span, + "if you want an empty slice, use", + format!("[] as &[{}]", slice_ty), + Applicability::MaybeIncorrect + ); + } + } + } ); } else { span_lint_and_then( diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index ee2cbc3cf54..332c0427ef6 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -4,18 +4,23 @@ const ANSWER: i32 = 42; fn main() { + let arr = [1, 2, 3, 4, 5]; + + // These should be linted: + (21..=42).rev().for_each(|x| println!("{}", x)); let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); for _ in (-42..=-21).rev() {} for _ in (21u32..42u32).rev() {} + let _ = &[] as &[i32]; + // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index 6ed5ca6daa0..901ec8bcc09 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -4,18 +4,23 @@ const ANSWER: i32 = 42; fn main() { + let arr = [1, 2, 3, 4, 5]; + + // These should be linted: + (42..=21).for_each(|x| println!("{}", x)); let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); for _ in -21..=-42 {} for _ in 42u32..21u32 {} + let _ = &arr[3..3]; + // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 97933b8ff85..9a646fd9939 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + --> $DIR/reversed_empty_ranges_fixable.rs:11:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + --> $DIR/reversed_empty_ranges_fixable.rs:12:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:10:14 + --> $DIR/reversed_empty_ranges_fixable.rs:14:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ^^^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + --> $DIR/reversed_empty_ranges_fixable.rs:15:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ @@ -43,5 +43,11 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_fixable.rs:17:18 + | +LL | let _ = &arr[3..3]; + | ----^^^^- help: if you want an empty slice, use: `[] as &[i32]` + +error: aborting due to 5 previous errors diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs index c9ca4c47668..561a35625f0 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -9,7 +9,6 @@ fn main() { let arr = [1, 2, 3, 4, 5]; let _ = &arr[3usize..=1usize]; let _ = &arr[SOME_NUM..1]; - let _ = &arr[3..3]; for _ in ANSWER..ANSWER {} } diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr index 12e5483ecdf..240188cbb46 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -18,17 +18,11 @@ error: this range is reversed and using it to index a slice will panic at run-ti LL | let _ = &arr[SOME_NUM..1]; | ^^^^^^^^^^^ -error: this range is empty and using it to index a slice will always yield an empty slice - --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 - | -LL | let _ = &arr[3..3]; - | ^^^^ - error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + --> $DIR/reversed_empty_ranges_unfixable.rs:13:14 | LL | for _ in ANSWER..ANSWER {} | ^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors From 6bd9cd99a3da53bdda4530dde9f737a843de6c91 Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Wed, 21 Aug 2019 21:18:43 +0200 Subject: [PATCH 0108/1110] Add tests --- tests/ui/or_fun_call.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7599b945a91..522f31b72d0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -95,6 +95,15 @@ fn test_or_with_ctors() { let b = "b".to_string(); let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) .or(Some(Bar(b, Duration::from_secs(2)))); + + let vec = vec!["foo"]; + let _ = opt.ok_or(vec.len()); + + let array = ["foo"]; + let _ = opt.ok_or(array.len()); + + let slice = &["foo"][..]; + let _ = opt.ok_or(slice.len()); } // Issue 4514 - early return From 566377f6272b0a3b9fa65dabe1f39ee82be80d4e Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Wed, 21 Aug 2019 21:19:28 +0200 Subject: [PATCH 0109/1110] Ignore calls to 'len' --- clippy_lints/src/methods/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 32b3b7f7947..c82cf57a4b1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1614,6 +1614,21 @@ fn lint_or_fun_call<'a, 'tcx>( or_has_args: bool, span: Span, ) { + if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.node { + if path.ident.as_str() == "len" { + let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); + + match ty.sty { + ty::Slice(_) | ty::Array(_, _) => return, + _ => (), + } + + if match_type(cx, ty, &paths::VEC) { + return; + } + } + } + // (path, fn_has_argument, methods, suffix) let know_types: &[(&[_], _, &[_], _)] = &[ (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), From bcfeb4de1589c19a7b21f04fec284e6045c0aa7a Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Mon, 25 May 2020 21:23:39 +0200 Subject: [PATCH 0110/1110] Fix build --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c82cf57a4b1..52ca962e7ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1614,11 +1614,11 @@ fn lint_or_fun_call<'a, 'tcx>( or_has_args: bool, span: Span, ) { - if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.node { + if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.kind { if path.ident.as_str() == "len" { let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); - match ty.sty { + match ty.kind { ty::Slice(_) | ty::Array(_, _) => return, _ => (), } From d9f55322cccf1e1ca1b996f8431f7ff8836d5d55 Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Mon, 25 May 2020 21:38:46 +0200 Subject: [PATCH 0111/1110] Update ui test --- tests/ui/or_fun_call.fixed | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 8ea03fe4261..7bb08797ef3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -95,6 +95,15 @@ fn test_or_with_ctors() { let b = "b".to_string(); let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) .or(Some(Bar(b, Duration::from_secs(2)))); + + let vec = vec!["foo"]; + let _ = opt.ok_or(vec.len()); + + let array = ["foo"]; + let _ = opt.ok_or(array.len()); + + let slice = &["foo"][..]; + let _ = opt.ok_or(slice.len()); } // Issue 4514 - early return From f2154e98379fcdd42ef226b6e19e9dc218422f83 Mon Sep 17 00:00:00 2001 From: returntrip Date: Mon, 25 May 2020 23:06:08 +0200 Subject: [PATCH 0112/1110] To make it easier for Linux distributions, ship the licenses text within each crate directory. --- rustc_tools_util/LICENSE-APACHE | 1 + rustc_tools_util/LICENSE-MIT | 1 + 2 files changed, 2 insertions(+) create mode 120000 rustc_tools_util/LICENSE-APACHE create mode 120000 rustc_tools_util/LICENSE-MIT diff --git a/rustc_tools_util/LICENSE-APACHE b/rustc_tools_util/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/rustc_tools_util/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/rustc_tools_util/LICENSE-MIT b/rustc_tools_util/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/rustc_tools_util/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file From a1824e187cb6d17e48e2ff039810551540a9b826 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 25 May 2020 23:09:06 +0200 Subject: [PATCH 0113/1110] ptr_arg: honor `allow` attr on arguments --- clippy_lints/src/ptr.rs | 10 +++++++++- clippy_lints/src/utils/sugg.rs | 2 +- tests/ui/ptr_arg.rs | 32 +++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2cdf9671419..4eac571f966 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -2,7 +2,7 @@ use crate::utils::ptr::get_spans; use crate::utils::{ - is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, + is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then, walk_ptrs_hir_ty, }; use if_chain::if_chain; @@ -150,8 +150,16 @@ fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_ let fn_def_id = cx.tcx.hir().local_def_id(fn_id); let sig = cx.tcx.fn_sig(fn_def_id); let fn_ty = sig.skip_binder(); + let body = opt_body_id.map(|id| cx.tcx.hir().body(id)); for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() { + // Honor the allow attribute on parameters. See issue 5644. + if let Some(body) = &body { + if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) { + continue; + } + } + if let ty::Ref(_, ty, Mutability::Not) = ty.kind { if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { let mut ty_snippet = None; diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 4ebe2e2852f..73758b7eeb7 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -530,7 +530,7 @@ pub trait DiagnosticBuilderExt<'a, T: LintContext> { /// Suggest to add an item before another. /// - /// The item should not be indented (expect for inner indentation). + /// The item should not be indented (except for inner indentation). /// /// # Example /// diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 30f39e9b063..541225e6351 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -71,7 +71,6 @@ fn false_positive_capacity_too(x: &String) -> String { #[allow(dead_code)] fn test_cow_with_ref(c: &Cow<[i32]>) {} -#[allow(dead_code)] fn test_cow(c: Cow<[i32]>) { let _c = c; } @@ -84,3 +83,34 @@ trait Foo2 { impl Foo2 for String { fn do_string(&self) {} } + +// Check that the allow attribute on parameters is honored +mod issue_5644 { + use std::borrow::Cow; + + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + + struct S {} + impl S { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } + + trait T { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } +} From 67167be1679c60eefa2c314c5e4a2b673d5eef11 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 17 May 2020 18:14:43 +0200 Subject: [PATCH 0114/1110] Make empty_line_after_outer_attr an early lint --- clippy_lints/Cargo.toml | 4 + clippy_lints/src/attrs.rs | 75 +++++++++++-------- tests/compile-test.rs | 2 +- tests/ui/auxiliary/proc_macro_attr.rs | 37 +++++++++ tests/ui/empty_line_after_outer_attribute.rs | 19 ++++- .../empty_line_after_outer_attribute.stderr | 12 +-- 6 files changed, 109 insertions(+), 40 deletions(-) create mode 100644 tests/ui/auxiliary/proc_macro_attr.rs diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 1c0be727834..043a79f2001 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -33,5 +33,9 @@ semver = "0.9.0" # see url = { version = "2.1.0", features = ["serde"] } +[dev-dependencies] +quote = "*" +syn = { version = "*", features = ["full"] } + [features] deny-warnings = [] diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 64abc9fdc71..41f125d4839 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -248,7 +248,6 @@ declare_lint_pass!(Attributes => [ INLINE_ALWAYS, DEPRECATED_SEMVER, USELESS_ATTRIBUTE, - EMPTY_LINE_AFTER_OUTER_ATTR, UNKNOWN_CLIPPY_LINTS, ]); @@ -480,36 +479,6 @@ fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attrib } for attr in attrs { - let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { - attr - } else { - continue; - }; - - if attr.style == AttrStyle::Outer { - if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { - return; - } - - let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt()); - let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt()); - - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { - let lines = snippet.split('\n').collect::>(); - let lines = without_block_comments(lines); - - if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - span_lint( - cx, - EMPTY_LINE_AFTER_OUTER_ATTR, - begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - ); - } - } - } - if let Some(values) = attr.meta_item_list() { if values.len() != 1 || !attr.check_name(sym!(inline)) { continue; @@ -551,15 +520,57 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } } -declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]); +declare_lint_pass!(EarlyAttributes => [ + DEPRECATED_CFG_ATTR, + MISMATCHED_TARGET_OS, + EMPTY_LINE_AFTER_OUTER_ATTR, +]); impl EarlyLintPass for EarlyAttributes { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + check_empty_line_after_outer_attr(cx, item); + } + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { check_deprecated_cfg_attr(cx, attr); check_mismatched_target_os(cx, attr); } } +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + for attr in &item.attrs { + let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { + attr + } else { + return; + }; + + if attr.style == AttrStyle::Outer { + if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { + return; + } + + let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt()); + let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt()); + + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + let lines = snippet.split('\n').collect::>(); + let lines = without_block_comments(lines); + + if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { + span_lint( + cx, + EMPTY_LINE_AFTER_OUTER_ATTR, + begin_of_attr_to_item, + "Found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + ); + } + } + } + } +} + fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { if_chain! { // check cfg_attr diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a5de8429390..2758b9a7e76 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -38,7 +38,7 @@ fn clippy_driver_path() -> PathBuf { // as what we manually pass to `cargo` invocation fn third_party_crates() -> String { use std::collections::HashMap; - static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"]; + static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"]; let dep_dir = cargo::TARGET_LIB.join("deps"); let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len()); for entry in fs::read_dir(dep_dir).unwrap() { diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs new file mode 100644 index 00000000000..e6626d57a77 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -0,0 +1,37 @@ +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![allow(clippy::useless_conversion)] + +extern crate proc_macro; +extern crate quote; +extern crate syn; + +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use syn::parse_macro_input; +use syn::{parse_quote, ItemTrait, TraitItem}; + +#[proc_macro_attribute] +pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut item = parse_macro_input!(input as ItemTrait); + for inner in &mut item.items { + if let TraitItem::Method(method) = inner { + let sig = &method.sig; + let block = &mut method.default; + if let Some(block) = block { + let brace = block.brace_token; + + let my_block = quote_spanned!( brace.span => { + // Should not trigger `empty_line_after_outer_attr` + #[crate_type = "lib"] + #sig #block + Vec::new() + }); + *block = parse_quote!(#my_block); + } + } + } + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 5343dff9da1..3e92bca986a 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -1,8 +1,12 @@ +// aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_outer_attr)] #![allow(clippy::assertions_on_constants)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +#[macro_use] +extern crate proc_macro_attr; + // This should produce a warning #[crate_type = "lib"] @@ -93,4 +97,17 @@ pub struct S; /* test */ pub struct T; -fn main() { } +// This should not produce a warning +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index d8c9786541f..bf753a732f0 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:7:1 + --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:19:1 + --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:24:1 + --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:31:1 + --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:39:1 + --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:47:1 + --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] LL | | From e3f6a8fc20ce778168e079257b7a33a47fe8541f Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 17 May 2020 19:09:07 +0200 Subject: [PATCH 0115/1110] Specify quote and syn versions --- clippy_lints/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 043a79f2001..11586083d8c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -34,8 +34,8 @@ semver = "0.9.0" url = { version = "2.1.0", features = ["serde"] } [dev-dependencies] -quote = "*" -syn = { version = "*", features = ["full"] } +quote = "1.0.2" +syn = { version = "1.0.11", features = ["full"] } [features] deny-warnings = [] From cdff59e156a85d86f7abb9834e42a18fe1ee257e Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 17 May 2020 19:13:33 +0200 Subject: [PATCH 0116/1110] Using dev-dependencies doesn't seem to work w/ compiletest --- clippy_lints/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 11586083d8c..7514608bc7e 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -32,8 +32,6 @@ semver = "0.9.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } - -[dev-dependencies] quote = "1.0.2" syn = { version = "1.0.11", features = ["full"] } From fd86b3150e21df8eb6fee2f0c8b69f323146ffad Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 26 May 2020 16:51:04 +0200 Subject: [PATCH 0117/1110] Be less specific about quote and syn versions --- clippy_lints/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 7514608bc7e..76baf27fb2d 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -32,8 +32,8 @@ semver = "0.9.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } -quote = "1.0.2" -syn = { version = "1.0.11", features = ["full"] } +quote = "1" +syn = { version = "1", features = ["full"] } [features] deny-warnings = [] From 1801841ae554a7778666c4c1085393b32eccf74d Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 26 May 2020 18:40:42 +0200 Subject: [PATCH 0118/1110] Add test cases for broader coverage --- clippy_lints/src/useless_conversion.rs | 10 ++++---- tests/ui/useless_conversion.stderr | 20 ++++++++-------- tests/ui/useless_conversion_try.rs | 8 +++++++ tests/ui/useless_conversion_try.stderr | 32 +++++++++++++++++++------- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1645c5777b2..7fa97b24699 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -71,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -87,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -108,7 +108,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", None, "consider removing `.try_into()`", ); @@ -139,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", None, &hint, ); @@ -158,7 +158,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 0b2947f7d62..84ec5370278 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,55 +10,55 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index ab4f960edb7..3787ea99144 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -31,4 +31,12 @@ fn main() { let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); let _: String = format!("Hello {}", "world").try_into().unwrap(); + let _: String = "".to_owned().try_into().unwrap(); + let _: String = match String::from("_").try_into() { + Ok(a) => a, + Err(_) => "".into(), + }; + // FIXME this is a false negative + #[allow(clippy::cmp_owned)] + if String::from("a") == TryInto::::try_into(String::from("a")).unwrap() {} } diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index 5afb5dc45d3..b765727c168 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,4 +1,4 @@ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:6:13 | LL | let _ = T::try_from(val).unwrap(); @@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider removing `T::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:7:5 | LL | val.try_into().unwrap() @@ -19,7 +19,7 @@ LL | val.try_into().unwrap() | = help: consider removing `.try_into()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:29:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); @@ -27,7 +27,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); | = help: consider removing `.try_into()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); @@ -35,7 +35,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | = help: consider removing `TryFrom::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); @@ -43,7 +43,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); | = help: consider removing `String::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); @@ -51,7 +51,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | = help: consider removing `String::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:33:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); @@ -59,5 +59,21 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | = help: consider removing `.try_into()` -error: aborting due to 7 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:34:21 + | +LL | let _: String = "".to_owned().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:35:27 + | +LL | let _: String = match String::from("_").try_into() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 9 previous errors From 7fd3bd0f57e11a65641501d6a898328ecb83ca77 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 24 Apr 2020 00:14:03 +0200 Subject: [PATCH 0119/1110] Register redundant_field_names and non_expressive_names as early passes --- clippy_lints/src/lib.rs | 12 ++++++------ src/driver.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 057d39d4c82..902f3d56c1e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -346,13 +346,8 @@ mod reexport { /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. /// /// Used in `./src/driver.rs`. -pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Conf) { +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); - store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; - store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames { - single_char_binding_names_threshold, - }); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); } @@ -1066,6 +1061,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_early_pass(|| box redundant_field_names::RedundantFieldNames); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; + store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { + single_char_binding_names_threshold, + }); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/src/driver.rs b/src/driver.rs index d3a7e24937f..70c47b42682 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -79,7 +79,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_lints::read_conf(&[], &sess); clippy_lints::register_plugins(&mut lint_store, &sess, &conf); - clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf); + clippy_lints::register_pre_expansion_lints(&mut lint_store); clippy_lints::register_renamed(&mut lint_store); })); From 8e22d15055231fc0df4a07d57cd883fd89d8131b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 May 2020 16:26:55 +0200 Subject: [PATCH 0120/1110] Fix fallout in redundant_field_names --- clippy_lints/src/redundant_field_names.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index b12c3c344ef..2a81170e49e 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -2,6 +2,7 @@ use crate::utils::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -36,6 +37,9 @@ declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess, expr.span) { + return; + } if let ExprKind::Struct(_, ref fields, _) = expr.kind { for field in fields { if field.is_shorthand { From 04db13eb564f6e3264a0d376ef95365b1de44797 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 May 2020 16:50:00 +0200 Subject: [PATCH 0121/1110] Fix fallout in similar_names --- clippy_lints/src/non_expressive_names.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 2b51b732075..5328773a738 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -5,6 +5,7 @@ use rustc_ast::ast::{ use rustc_ast::attr; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{Ident, SymbolStr}; @@ -354,12 +355,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { impl EarlyLintPass for NonExpressiveNames { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if in_external_macro(cx.sess, item.span) { + return; + } + if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } } fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) { + if in_external_macro(cx.sess, item.span) { + return; + } + if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } From 0ad08109fd1c0b72d8bde3291271e4f2c8dbe66e Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Wed, 27 May 2020 06:25:38 +0900 Subject: [PATCH 0122/1110] Bump actions/cache from v1 to v2 --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 8edf0c23860..5fa8009a8b4 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -49,7 +49,7 @@ jobs: run: cargo update - name: Cache cargo dir - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cargo key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 6675a1029bb..a8a673343bf 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -94,7 +94,7 @@ jobs: run: cargo update - name: Cache cargo dir - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cargo key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }} @@ -190,7 +190,7 @@ jobs: run: cargo update - name: Cache cargo dir - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cargo key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} @@ -269,7 +269,7 @@ jobs: run: cargo update - name: Cache cargo dir - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cargo key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} From 416182347589e9503408136747593ff95fb9dd13 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 27 May 2020 00:06:50 +0200 Subject: [PATCH 0123/1110] Avoid triggering similar names on code from expansion --- clippy_lints/src/new_without_default.rs | 10 +++++----- clippy_lints/src/non_expressive_names.rs | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 3b88e4c4cb1..e556e5d59c1 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -90,8 +90,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { return; } if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) { - let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); - let self_ty = cx.tcx.type_of(self_did); + let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); + let self_ty = cx.tcx.type_of(self_def_id); if_chain! { if same_tys(cx, self_ty, return_ty(cx, id)); if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); @@ -112,10 +112,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { // generics if_chain! { if let Some(ref impling_types) = self.impling_types; - if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def(); - if let Some(self_def_id) = self_def.did.as_local(); + if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def(); + if let Some(self_local_did) = self_def.did.as_local(); then { - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id); + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); if impling_types.contains(&self_id) { return; } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 5328773a738..5f14fe97afe 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -132,7 +132,11 @@ struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { fn visit_pat(&mut self, pat: &'tcx Pat) { match pat.kind { - PatKind::Ident(_, ident, _) => self.check_ident(ident), + PatKind::Ident(_, ident, _) => { + if !pat.span.from_expansion() { + self.check_ident(ident); + } + }, PatKind::Struct(_, ref fields, _) => { for field in fields { if !field.is_shorthand { From 58429c74a31fabde8555f940530039bdadde8400 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 27 May 2020 00:51:08 +0200 Subject: [PATCH 0124/1110] Fail bors on missing changelog --- .github/workflows/clippy_bors.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 6675a1029bb..eb8da9dcc88 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -312,7 +312,7 @@ jobs: name: bors test finished if: github.event.pusher.name == 'bors' && success() runs-on: ubuntu-latest - needs: [base, integration] + needs: [changelog, base, integration_build, integration] steps: - name: Mark the job as successful @@ -322,7 +322,7 @@ jobs: name: bors test finished if: github.event.pusher.name == 'bors' && (failure() || cancelled()) runs-on: ubuntu-latest - needs: [base, integration] + needs: [changelog, base, integration_build, integration] steps: - name: Mark the job as a failure From 3089c3b3077fa8ae0b6f68c5f56650bf726e3298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 27 May 2020 13:55:57 +0200 Subject: [PATCH 0125/1110] rustup https://github.com/rust-lang/rust/pull/72342, allow unused_crate_dependencies --- tests/ui/cognitive_complexity.rs | 2 +- tests/ui/cognitive_complexity_attr_used.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 1d3fe405521..912e6788afd 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -1,6 +1,6 @@ #![allow(clippy::all)] #![warn(clippy::cognitive_complexity)] -#![allow(unused)] +#![allow(unused, unused_crate_dependencies)] #[rustfmt::skip] fn main() { diff --git a/tests/ui/cognitive_complexity_attr_used.rs b/tests/ui/cognitive_complexity_attr_used.rs index 403eff566ed..771a26fc9a8 100644 --- a/tests/ui/cognitive_complexity_attr_used.rs +++ b/tests/ui/cognitive_complexity_attr_used.rs @@ -1,5 +1,5 @@ -#![warn(clippy::cognitive_complexity)] -#![warn(unused)] +#![warn(unused, clippy::cognitive_complexity)] +#![allow(unused_crate_dependencies)] fn main() { kaboom(); From 64a05f56c33d4754808ef85e634f72a9053c56fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 28 May 2020 00:36:15 +0200 Subject: [PATCH 0126/1110] len_zero: skip ranges if feature `range_is_empty` is not enabled --- clippy_lints/src/len_zero.rs | 17 ++++++++++++++++- tests/ui/len_zero.fixed | 8 ++++++++ tests/ui/len_zero.rs | 8 ++++++++ tests/ui/len_zero_ranges.fixed | 14 ++++++++++++++ tests/ui/len_zero_ranges.rs | 14 ++++++++++++++ tests/ui/len_zero_ranges.stderr | 10 ++++++++++ 6 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/ui/len_zero_ranges.fixed create mode 100644 tests/ui/len_zero_ranges.rs create mode 100644 tests/ui/len_zero_ranges.stderr diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 2ec0b5a8d6f..f5bfede75a7 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -259,6 +259,17 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. + fn should_skip_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + higher::range(cx, expr).map_or(false, |_| { + !cx.tcx + .features() + .declared_lib_features + .iter() + .any(|(name, _)| name.as_str() == "range_is_empty") + }) + } + /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssocItem) -> bool { if let ty::AssocKind::Fn = item.kind { @@ -284,6 +295,10 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } + if should_skip_range(cx, expr) { + return false; + } + let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => { diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 624e5ef8fcf..a29b832eb60 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,3 +141,11 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 7fba971cfd8..8fd0093f4fd 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,3 +141,11 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed new file mode 100644 index 00000000000..7da26f8ff4d --- /dev/null +++ b/tests/ui/len_zero_ranges.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).is_empty(); + } +} + +fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs new file mode 100644 index 00000000000..be7b4244bc0 --- /dev/null +++ b/tests/ui/len_zero_ranges.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).len() == 0; + } +} + +fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr new file mode 100644 index 00000000000..6e5fa41fb08 --- /dev/null +++ b/tests/ui/len_zero_ranges.stderr @@ -0,0 +1,10 @@ +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:10:17 + | +LL | let _ = (0..42).len() == 0; + | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + +error: aborting due to previous error + From 7b490903809ce5c03c83869357a68e88f8cc0799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 27 May 2020 14:08:31 +0200 Subject: [PATCH 0127/1110] clippy_dev: add ra_setup This takes an absolute path to a rustc repo and adds path-dependencies that point towards the respective rustc subcrates into the Cargo.tomls of the clippy and clippy_lints crate. This allows rustc-analyzer to show proper type annotations etc on rustc-internals inside the clippy repo. Usage: cargo dev ra-setup /absolute/path/to/rust/ cc https://github.com/rust-analyzer/rust-analyzer/issues/3517 cc https://github.com/rust-lang/rust-clippy/issues/5514 --- clippy_dev/src/lib.rs | 3 +- clippy_dev/src/main.rs | 16 ++++++- clippy_dev/src/ra_setup.rs | 90 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 clippy_dev/src/ra_setup.rs diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 6fdd282c684..5baa31d5cde 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -11,6 +11,7 @@ use walkdir::WalkDir; pub mod fmt; pub mod new_lint; +pub mod ra_setup; pub mod stderr_length_check; pub mod update_lints; @@ -400,7 +401,7 @@ fn test_replace_region_no_changes() { changed: false, new_lines: "123\n456\n789".to_string(), }; - let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || vec![]); + let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new); assert_eq!(expected, result); } diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d99235f7c07..281037ae37c 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints}; +use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints}; fn main() { let matches = App::new("Clippy developer tooling") @@ -87,6 +87,19 @@ fn main() { SubCommand::with_name("limit_stderr_length") .about("Ensures that stderr files do not grow longer than a certain amount of lines."), ) + .subcommand( + SubCommand::with_name("ra-setup") + .about("Alter dependencies so rust-analyzer can find rustc internals") + .arg( + Arg::with_name("rustc-repo-path") + .long("repo-path") + .short("r") + .help("The path to a rustc repo that will be used for setting the dependencies") + .takes_value(true) + .value_name("path") + .required(true), + ), + ) .get_matches(); match matches.subcommand() { @@ -115,6 +128,7 @@ fn main() { ("limit_stderr_length", _) => { stderr_length_check::check(); }, + ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), _ => {}, } } diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs new file mode 100644 index 00000000000..8617445c8a6 --- /dev/null +++ b/clippy_dev/src/ra_setup.rs @@ -0,0 +1,90 @@ +#![allow(clippy::filter_map)] + +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; + +// This module takes an absolute path to a rustc repo and alters the dependencies to point towards +// the respective rustc subcrates instead of using extern crate xyz. +// This allows rust analyzer to analyze rustc internals and show proper information inside clippy +// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details + +pub fn run(rustc_path: Option<&str>) { + // we can unwrap here because the arg is required here + let rustc_path = PathBuf::from(rustc_path.unwrap()); + assert!(rustc_path.is_dir(), "path is not a directory"); + let rustc_source_basedir = rustc_path.join("src"); + assert!( + rustc_source_basedir.is_dir(), + "are you sure the path leads to a rustc repo?" + ); + + let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml"); + let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "Cargo.toml", + &clippy_root_manifest, + &clippy_root_lib_rs, + ) + .expect("Failed to inject deps into ./Cargo.toml"); + + let clippy_lints_manifest = + fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml"); + let clippy_lints_lib_rs = + fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "clippy_lints/Cargo.toml", + &clippy_lints_manifest, + &clippy_lints_lib_rs, + ) + .expect("Failed to inject deps into ./clippy_lints/Cargo.toml"); +} + +fn inject_deps_into_manifest( + rustc_source_dir: &PathBuf, + manifest_path: &str, + cargo_toml: &str, + lib_rs: &str, +) -> std::io::Result<()> { + let extern_crates = lib_rs + .lines() + // get the deps + .filter(|line| line.starts_with("extern crate")) + // we have something like "extern crate foo;", we only care about the "foo" + // ↓ ↓ + // extern crate rustc_middle; + .map(|s| &s[13..(s.len() - 1)]); + + let new_deps = extern_crates.map(|dep| { + // format the dependencies that are going to be put inside the Cargo.toml + format!( + "{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n", + dep = dep, + source_path = rustc_source_dir.display() + ) + }); + + // format a new [dependencies]-block with the new deps we need to inject + let mut all_deps = String::from("[dependencies]\n"); + new_deps.for_each(|dep_line| { + all_deps.push_str(&dep_line); + }); + + // replace "[dependencies]" with + // [dependencies] + // dep1 = { path = ... } + // dep2 = { path = ... } + // etc + let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); + + // println!("{}", new_manifest); + let mut file = File::create(manifest_path)?; + file.write_all(new_manifest.as_bytes())?; + + println!("Dependency paths injected: {}", manifest_path); + + Ok(()) +} From b92cc8a08d74fb412bc444a4361df51b0c95401c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 29 May 2020 22:46:05 +0200 Subject: [PATCH 0128/1110] add testcase that no longer ICEs Fixes #3969 --- tests/ui/crashes/ice-3969.rs | 51 ++++++++++++++++++++++++++++++++ tests/ui/crashes/ice-3969.stderr | 22 ++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/ui/crashes/ice-3969.rs create mode 100644 tests/ui/crashes/ice-3969.stderr diff --git a/tests/ui/crashes/ice-3969.rs b/tests/ui/crashes/ice-3969.rs new file mode 100644 index 00000000000..4feab7910b7 --- /dev/null +++ b/tests/ui/crashes/ice-3969.rs @@ -0,0 +1,51 @@ +// https://github.com/rust-lang/rust-clippy/issues/3969 +// used to crash: error: internal compiler error: +// src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs + +// Check that tautalogically false bounds are accepted, and are used +// in type inference. +#![feature(trivial_bounds)] +#![allow(unused)] + +trait A {} + +impl A for i32 {} + +struct Dst { + x: X, +} + +struct TwoStrs(str, str) +where + str: Sized; + +fn unsized_local() +where + for<'a> Dst: Sized, +{ + let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +} + +fn return_str() -> str +where + str: Sized, +{ + *"Sized".to_string().into_boxed_str() +} + +fn use_op(s: String) -> String +where + String: ::std::ops::Neg, +{ + -s +} + +fn use_for() +where + i32: Iterator, +{ + for _ in 2i32 {} +} + +fn main() {} diff --git a/tests/ui/crashes/ice-3969.stderr b/tests/ui/crashes/ice-3969.stderr new file mode 100644 index 00000000000..923db0664a7 --- /dev/null +++ b/tests/ui/crashes/ice-3969.stderr @@ -0,0 +1,22 @@ +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:25:17 + | +LL | for<'a> Dst: Sized, + | ^^^^^^ help: use `dyn`: `dyn A + 'a` + | + = note: `-D bare-trait-objects` implied by `-D warnings` + +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:27:16 + | +LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); + | ^ help: use `dyn`: `dyn A` + +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:27:57 + | +LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); + | ^ help: use `dyn`: `dyn A` + +error: aborting due to 3 previous errors + From 5faab874f9f8655c8f284944b5acdede5c088af4 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 23 May 2020 00:07:09 +0200 Subject: [PATCH 0129/1110] new lint: vec_resize_to_zero --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/utils/paths.rs | 1 + clippy_lints/src/vec_resize_to_zero.rs | 59 ++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/vec_resize_to_zero.rs | 15 +++++++ tests/ui/vec_resize_to_zero.stderr | 13 ++++++ 7 files changed, 101 insertions(+) create mode 100644 clippy_lints/src/vec_resize_to_zero.rs create mode 100644 tests/ui/vec_resize_to_zero.rs create mode 100644 tests/ui/vec_resize_to_zero.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac9057199f..f7dae3dcfff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1630,6 +1630,7 @@ Released 2018-09-13 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 902f3d56c1e..4f0ecab393d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -325,6 +325,7 @@ mod unwrap; mod use_self; mod useless_conversion; mod vec; +mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; mod wildcard_imports; @@ -847,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::OUTER_EXPN_EXPN_DATA, &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, + &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, &wildcard_dependencies::WILDCARD_DEPENDENCIES, &wildcard_imports::ENUM_GLOB_USE, @@ -1062,6 +1064,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); + store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1430,6 +1433,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), @@ -1677,6 +1681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), + LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), ]); store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 779da7e6bf2..3b7e9739211 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -138,5 +138,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs new file mode 100644 index 00000000000..86cbfa8203d --- /dev/null +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -0,0 +1,59 @@ +use crate::utils::span_lint_and_then; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +use crate::utils::{match_def_path, paths}; +use rustc_ast::ast::LitKind; +use rustc_hir as hir; + +declare_clippy_lint! { + /// **What it does:** Finds occurences of `Vec::resize(0, an_int)` + /// + /// **Why is this bad?** This is probably an argument inversion mistake. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// vec!(1, 2, 3, 4, 5).resize(0, 5) + /// ``` + pub VEC_RESIZE_TO_ZERO, + correctness, + "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake" +} + +declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VecResizeToZero { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(path_segment, _, ref args) = expr.kind; + if let Some(method_def_id) = cx.tables.type_dependent_def_id(expr.hir_id); + if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3; + if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; + if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind; + then { + let method_call_span = expr.span.with_lo(path_segment.ident.span.lo()); + span_lint_and_then( + cx, + VEC_RESIZE_TO_ZERO, + expr.span, + "emptying a vector with `resize`", + |db| { + db.help("the arguments may be inverted..."); + db.span_suggestion( + method_call_span, + "...or you can empty the vector with", + "clear()".to_string(), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f63301c7db0..1e94ca00c14 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2460,6 +2460,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "vec_resize_to_zero", + group: "correctness", + desc: "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake", + deprecation: None, + module: "vec_resize_to_zero", + }, Lint { name: "verbose_bit_mask", group: "style", diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs new file mode 100644 index 00000000000..0263e2f5f20 --- /dev/null +++ b/tests/ui/vec_resize_to_zero.rs @@ -0,0 +1,15 @@ +#![warn(clippy::vec_resize_to_zero)] + +fn main() { + // applicable here + vec![1, 2, 3, 4, 5].resize(0, 5); + + // not applicable + vec![1, 2, 3, 4, 5].resize(2, 5); + + // applicable here, but only implemented for integer litterals for now + vec!["foo", "bar", "baz"].resize(0, "bar"); + + // not applicable + vec!["foo", "bar", "baz"].resize(2, "bar") +} diff --git a/tests/ui/vec_resize_to_zero.stderr b/tests/ui/vec_resize_to_zero.stderr new file mode 100644 index 00000000000..feb846298c6 --- /dev/null +++ b/tests/ui/vec_resize_to_zero.stderr @@ -0,0 +1,13 @@ +error: emptying a vector with `resize` + --> $DIR/vec_resize_to_zero.rs:5:5 + | +LL | vec![1, 2, 3, 4, 5].resize(0, 5); + | ^^^^^^^^^^^^^^^^^^^^------------ + | | + | help: ...or you can empty the vector with: `clear()` + | + = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings` + = help: the arguments may be inverted... + +error: aborting due to previous error + From 37381d33a4761a064311dd95fbc54b5da6ad3766 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 14:05:57 +0200 Subject: [PATCH 0130/1110] Fix sync fallout --- clippy_lints/src/needless_pass_by_value.rs | 8 +------- clippy_lints/src/write.rs | 13 ++++++------- tests/compile-test.rs | 7 ++++--- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 218b0d27f74..9c508fc0e4a 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -173,13 +173,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { !preds.is_empty() && { let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty); preds.iter().all(|t| { - let ty_params = &t - .skip_binder() - .trait_ref - .substs - .iter() - .skip(1) - .collect::>(); + let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::>(); implements_trait(cx, ty_empty_region, t.def_id(), ty_params) }) }, diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index dfa6223f1b9..5f794598052 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -279,13 +279,12 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - move || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability), - ); + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 1c4914a470c..7bd5f09f333 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -153,9 +153,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -216,6 +213,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); From 0bcfae92f80d31cad4e5fb687da8033a38d06a32 Mon Sep 17 00:00:00 2001 From: djugei Date: Sun, 31 May 2020 15:41:33 +0200 Subject: [PATCH 0131/1110] moved cast_ptr_alignment to pedantic and expanded documentation --- clippy_lints/src/types.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6ed9ff22e46..3ac99e24684 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -974,7 +974,8 @@ declare_clippy_lint! { /// behavior. /// /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar - /// on the resulting pointer is fine. + /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like + /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. /// /// **Example:** /// ```rust @@ -982,7 +983,7 @@ declare_clippy_lint! { /// let _ = (&mut 1u8 as *mut u8) as *mut u16; /// ``` pub CAST_PTR_ALIGNMENT, - correctness, + pedantic, "cast from a pointer to a more-strictly-aligned pointer" } From 18b5ceed7991e3d8616b74b42de26330ca4c40db Mon Sep 17 00:00:00 2001 From: djugei Date: Sun, 31 May 2020 16:00:29 +0200 Subject: [PATCH 0132/1110] ran update_lints --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 902f3d56c1e..6475fa67d25 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1164,6 +1164,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_POSSIBLE_TRUNCATION), LintId::of(&types::CAST_POSSIBLE_WRAP), LintId::of(&types::CAST_PRECISION_LOSS), + LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_SIGN_LOSS), LintId::of(&types::IMPLICIT_HASHER), LintId::of(&types::INVALID_UPCAST_COMPARISONS), @@ -1410,7 +1411,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::BORROWED_BOX), LintId::of(&types::BOX_VEC), - LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), @@ -1669,7 +1669,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), LintId::of(&types::ABSURD_EXTREME_COMPARISONS), - LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), LintId::of(&unicode::ZERO_WIDTH_SPACE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f63301c7db0..b9c84654593 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -166,7 +166,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "cast_ptr_alignment", - group: "correctness", + group: "pedantic", desc: "cast from a pointer to a more-strictly-aligned pointer", deprecation: None, module: "types", From 6122612232976c1ac766a5d415265eb3eb30e72c Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 31 May 2020 17:38:59 +0200 Subject: [PATCH 0133/1110] Increase cargo_metadata version to 0.9.1 `clippy_lints` makes use of `dep_kinds` on `NodeDep` but this was only added in versoin 0.9.1. Compiling with 0.9.0 will fail because of this. --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6999b6bd740..836897927b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" [dev-dependencies] -cargo_metadata = "0.9.0" +cargo_metadata = "0.9.1" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" lazy_static = "1.0" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 76baf27fb2d..98391732d18 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2018" [dependencies] -cargo_metadata = "0.9.0" +cargo_metadata = "0.9.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" From 0ab823c509897ce2f516feb760fe1bf02cf77443 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 26 Aug 2019 18:34:30 +0200 Subject: [PATCH 0134/1110] Rework suggestion generation of `unit_arg` lint --- clippy_lints/src/types.rs | 42 ++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3ac99e24684..8fcca4b7bb9 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -779,6 +779,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { match expr.kind { ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => { + let mut args_to_recover = vec![]; for arg in args { if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { if let ExprKind::Match(.., match_source) = &arg.kind { @@ -787,17 +788,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { } } - span_lint_and_sugg( - cx, - UNIT_ARG, - arg.span, - "passing a unit value to a function", - "if you intended to pass a unit value, use a unit literal instead", - "()".to_string(), - Applicability::MaybeIncorrect, - ); + args_to_recover.push(arg); } } + if !args_to_recover.is_empty() { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + "move the expressions in front of the call...", + format!( + "{} ", + args_to_recover + .iter() + .map(|arg| { + format!( + "{};", + snippet_with_applicability(cx, arg.span, "..", &mut applicability) + ) + }) + .collect::>() + .join(" ") + ), + applicability, + ); + db.multipart_suggestion( + "...and use unit literals instead", + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + }); + } }, _ => (), } From 380d941a045dc213ae28807d74fc32d1b1841e22 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 26 Aug 2019 19:35:25 +0200 Subject: [PATCH 0135/1110] Adapt stderr and fixed files --- tests/ui/unit_arg.fixed | 29 +++++++--- tests/ui/unit_arg.rs | 10 +++- tests/ui/unit_arg.stderr | 112 ++++++++++++++++++++++++++++++--------- 3 files changed, 118 insertions(+), 33 deletions(-) diff --git a/tests/ui/unit_arg.fixed b/tests/ui/unit_arg.fixed index a739cf7ad81..67c6bdf8873 100644 --- a/tests/ui/unit_arg.fixed +++ b/tests/ui/unit_arg.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(unused_braces, clippy::no_effect, unused_must_use)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] use std::fmt::Debug; @@ -21,13 +21,21 @@ impl Bar { } fn bad() { - foo(()); - foo(()); - foo(()); - foo(()); - foo3((), 2, 2); + {}; foo(()); + { + 1; + }; foo(()); + foo(1); foo(()); + { + foo(1); + foo(2); + }; foo(()); + {}; foo3((), 2, 2); let b = Bar; - b.bar(()); + { + 1; + }; b.bar(()); + foo(0); foo(1); taking_multiple_units((), ()); } fn ok() { @@ -58,6 +66,13 @@ mod issue_2945 { } } +#[allow(dead_code)] +fn returning_expr() -> Option<()> { + foo(1); Some(()) +} + +fn taking_multiple_units(a: (), b: ()) {} + fn main() { bad(); ok(); diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index d90c49f79de..c6e465b2e4c 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(unused_braces, clippy::no_effect, unused_must_use)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] use std::fmt::Debug; @@ -35,6 +35,7 @@ fn bad() { b.bar({ 1; }); + taking_multiple_units(foo(0), foo(1)); } fn ok() { @@ -65,6 +66,13 @@ mod issue_2945 { } } +#[allow(dead_code)] +fn returning_expr() -> Option<()> { + Some(foo(1)) +} + +fn taking_multiple_units(a: (), b: ()) {} + fn main() { bad(); ok(); diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 21ccc684ea9..ce9ab2f1271 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,79 +1,141 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:24:9 + --> $DIR/unit_arg.rs:24:5 | LL | foo({}); - | ^^ + | ^^^^^^^ | = note: `-D clippy::unit-arg` implied by `-D warnings` -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | {}; foo({}); + | ^^^ +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:25:9 + --> $DIR/unit_arg.rs:25:5 | -LL | foo({ - | _________^ +LL | / foo({ LL | | 1; LL | | }); - | |_____^ + | |______^ | -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | { +LL | 1; +LL | }; foo({ + | +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:28:9 + --> $DIR/unit_arg.rs:28:5 | LL | foo(foo(1)); - | ^^^^^^ + | ^^^^^^^^^^^ | -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | foo(1); foo(foo(1)); + | ^^^^^^^ +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:29:9 + --> $DIR/unit_arg.rs:29:5 | -LL | foo({ - | _________^ +LL | / foo({ LL | | foo(1); LL | | foo(2); LL | | }); - | |_____^ + | |______^ | -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | { +LL | foo(1); +LL | foo(2); +LL | }; foo({ + | +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:10 + --> $DIR/unit_arg.rs:33:5 | LL | foo3({}, 2, 2); - | ^^ + | ^^^^^^^^^^^^^^ | -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | {}; foo3({}, 2, 2); + | ^^^ +help: ...and use unit literals instead | LL | foo3((), 2, 2); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:11 + --> $DIR/unit_arg.rs:35:5 | -LL | b.bar({ - | ___________^ +LL | / b.bar({ LL | | 1; LL | | }); - | |_____^ + | |______^ | -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | { +LL | 1; +LL | }; b.bar({ + | +help: ...and use unit literals instead | LL | b.bar(()); | ^^ -error: aborting due to 6 previous errors +error: passing a unit value to a function + --> $DIR/unit_arg.rs:38:5 + | +LL | taking_multiple_units(foo(0), foo(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(0); foo(1); taking_multiple_units(foo(0), foo(1)); + | ^^^^^^^^^^^^^^^ +help: ...and use unit literals instead + | +LL | taking_multiple_units((), foo(1)); + | ^^ +help: ...and use unit literals instead + | +LL | taking_multiple_units(foo(0), ()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:71:5 + | +LL | Some(foo(1)) + | ^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(1); Some(foo(1)) + | ^^^^^^^ +help: ...and use unit literals instead + | +LL | Some(()) + | ^^ + +error: aborting due to 8 previous errors From a1a1a4b82a35b810570dbf7d2ee7f00896bee232 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 26 Aug 2019 21:43:29 +0200 Subject: [PATCH 0136/1110] Use multiple span_suggestions instead of multipart_suggestion multipart suggestions aren't autofixable by rustfix yet --- clippy_lints/src/types.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 8fcca4b7bb9..3fbea77757d 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -812,14 +812,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { ), applicability, ); - db.multipart_suggestion( - "...and use unit literals instead", - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); + for arg in args_to_recover { + db.span_suggestion( + arg.span, + "...and use unit literals instead", + "()".to_string(), + applicability, + ); + } }); } }, From 0f69cafc2dd77d573e24870887a4a13cfe50515a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 17 Feb 2020 18:10:59 +0100 Subject: [PATCH 0137/1110] Rework suggestion generation and use multipart_suggestion again --- clippy_lints/src/types.rs | 55 +++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3fbea77757d..c95bd5d72bc 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -794,32 +794,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { if !args_to_recover.is_empty() { let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { + let sugg = args_to_recover + .iter() + .enumerate() + .map(|(i, arg)| { + let indent = if i == 0 { + 0 + } else { + indent_of(cx, expr.span).unwrap_or(0) + }; + format!( + "{}{};", + " ".repeat(indent), + snippet_block_with_applicability( + cx, + arg.span, + "..", + Some(expr.span), + &mut applicability + ) + ) + }) + .collect::>() + .join("\n"); db.span_suggestion( expr.span.with_hi(expr.span.lo()), - "move the expressions in front of the call...", - format!( - "{} ", - args_to_recover - .iter() - .map(|arg| { - format!( - "{};", - snippet_with_applicability(cx, arg.span, "..", &mut applicability) - ) - }) - .collect::>() - .join(" ") - ), + &format!("{}move the expressions in front of the call...", or), + format!("{}\n", sugg), + applicability, + ); + db.multipart_suggestion( + "...and use unit literals instead", + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), applicability, ); - for arg in args_to_recover { - db.span_suggestion( - arg.span, - "...and use unit literals instead", - "()".to_string(), - applicability, - ); - } }); } }, From f9c325f5b657e0c37ba2016a51cddbeab7f7693f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 17 Feb 2020 18:11:50 +0100 Subject: [PATCH 0138/1110] Suggest to remove the semicolon of the last stmt in a block --- clippy_lints/src/types.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c95bd5d72bc..51d7d9b3ab7 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -794,6 +794,36 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { if !args_to_recover.is_empty() { let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { + let mut or = ""; + args_to_recover + .iter() + .filter_map(|arg| { + if_chain! { + if let ExprKind::Block(block, _) = arg.kind; + if block.expr.is_none(); + if let Some(last_stmt) = block.stmts.iter().last(); + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let Some(snip) = snippet_opt(cx, last_expr.span); + then { + Some(( + last_stmt.span, + snip, + )) + } + else { + None + } + } + }) + .for_each(|(span, sugg)| { + db.span_suggestion( + span, + "remove the semicolon from the last statement in the block", + sugg, + Applicability::MaybeIncorrect, + ); + or = "or "; + }); let sugg = args_to_recover .iter() .enumerate() From 4c9cefa12232aa0224b1680f51654fe10f5cf3b7 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 18 Feb 2020 09:51:52 +0100 Subject: [PATCH 0139/1110] Move linting out in its own function --- clippy_lints/src/types.rs | 171 ++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 80 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 51d7d9b3ab7..6866635b904 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -779,89 +779,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { match expr.kind { ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => { - let mut args_to_recover = vec![]; - for arg in args { - if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { - if let ExprKind::Match(.., match_source) = &arg.kind { - if *match_source == MatchSource::TryDesugar { - continue; + let args_to_recover = args + .iter() + .filter(|arg| { + if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { + if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind { + false + } else { + true } + } else { + false } - - args_to_recover.push(arg); - } - } + }) + .collect::>(); if !args_to_recover.is_empty() { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { - let mut or = ""; - args_to_recover - .iter() - .filter_map(|arg| { - if_chain! { - if let ExprKind::Block(block, _) = arg.kind; - if block.expr.is_none(); - if let Some(last_stmt) = block.stmts.iter().last(); - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let Some(snip) = snippet_opt(cx, last_expr.span); - then { - Some(( - last_stmt.span, - snip, - )) - } - else { - None - } - } - }) - .for_each(|(span, sugg)| { - db.span_suggestion( - span, - "remove the semicolon from the last statement in the block", - sugg, - Applicability::MaybeIncorrect, - ); - or = "or "; - }); - let sugg = args_to_recover - .iter() - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability( - cx, - arg.span, - "..", - Some(expr.span), - &mut applicability - ) - ) - }) - .collect::>() - .join("\n"); - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expressions in front of the call...", or), - format!("{}\n", sugg), - applicability, - ); - db.multipart_suggestion( - "...and use unit literals instead", - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); - }); + lint_unit_args(cx, expr, &args_to_recover); } }, _ => (), @@ -869,6 +802,84 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { } } +fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + let (singular, plural) = if args_to_recover.len() > 1 { + ("", "s") + } else { + ("a ", "") + }; + span_lint_and_then( + cx, + UNIT_ARG, + expr.span, + &format!("passing {}unit value{} to a function", singular, plural), + |db| { + let mut or = ""; + args_to_recover + .iter() + .filter_map(|arg| { + if_chain! { + if let ExprKind::Block(block, _) = arg.kind; + if block.expr.is_none(); + if let Some(last_stmt) = block.stmts.iter().last(); + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let Some(snip) = snippet_opt(cx, last_expr.span); + then { + Some(( + last_stmt.span, + snip, + )) + } + else { + None + } + } + }) + .for_each(|(span, sugg)| { + db.span_suggestion( + span, + "remove the semicolon from the last statement in the block", + sugg, + Applicability::MaybeIncorrect, + ); + or = "or "; + }); + let sugg = args_to_recover + .iter() + .enumerate() + .map(|(i, arg)| { + let indent = if i == 0 { + 0 + } else { + indent_of(cx, expr.span).unwrap_or(0) + }; + format!( + "{}{};", + " ".repeat(indent), + snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) + ) + }) + .collect::>() + .join("\n"); + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + &format!("{}move the expression{} in front of the call...", or, plural), + format!("{}\n", sugg), + applicability, + ); + db.multipart_suggestion( + &format!("...and use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + }, + ); +} + fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::DesugaringKind; if let ExprKind::Call(ref callee, _) = expr.kind { From 6d15a149640e5647ce232690d54b540346fa1641 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 17 Feb 2020 18:12:01 +0100 Subject: [PATCH 0140/1110] Update test files --- tests/ui/unit_arg.fixed | 79 ------------------ tests/ui/unit_arg.rs | 15 +++- tests/ui/unit_arg.stderr | 170 +++++++++++++++++++++++++++------------ 3 files changed, 134 insertions(+), 130 deletions(-) delete mode 100644 tests/ui/unit_arg.fixed diff --git a/tests/ui/unit_arg.fixed b/tests/ui/unit_arg.fixed deleted file mode 100644 index 67c6bdf8873..00000000000 --- a/tests/ui/unit_arg.fixed +++ /dev/null @@ -1,79 +0,0 @@ -// run-rustfix -#![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] - -use std::fmt::Debug; - -fn foo(t: T) { - println!("{:?}", t); -} - -fn foo3(t1: T1, t2: T2, t3: T3) { - println!("{:?}, {:?}, {:?}", t1, t2, t3); -} - -struct Bar; - -impl Bar { - fn bar(&self, t: T) { - println!("{:?}", t); - } -} - -fn bad() { - {}; foo(()); - { - 1; - }; foo(()); - foo(1); foo(()); - { - foo(1); - foo(2); - }; foo(()); - {}; foo3((), 2, 2); - let b = Bar; - { - 1; - }; b.bar(()); - foo(0); foo(1); taking_multiple_units((), ()); -} - -fn ok() { - foo(()); - foo(1); - foo({ 1 }); - foo3("a", 3, vec![3]); - let b = Bar; - b.bar({ 1 }); - b.bar(()); - question_mark(); -} - -fn question_mark() -> Result<(), ()> { - Ok(Ok(())?)?; - Ok(Ok(()))??; - Ok(()) -} - -#[allow(dead_code)] -mod issue_2945 { - fn unit_fn() -> Result<(), i32> { - Ok(()) - } - - fn fallible() -> Result<(), i32> { - Ok(unit_fn()?) - } -} - -#[allow(dead_code)] -fn returning_expr() -> Option<()> { - foo(1); Some(()) -} - -fn taking_multiple_units(a: (), b: ()) {} - -fn main() { - bad(); - ok(); -} diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index c6e465b2e4c..7d1b99fedc9 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,4 +1,3 @@ -// run-rustfix #![warn(clippy::unit_arg)] #![allow(clippy::no_effect, unused_must_use, unused_variables)] @@ -36,6 +35,20 @@ fn bad() { 1; }); taking_multiple_units(foo(0), foo(1)); + taking_multiple_units(foo(0), { + foo(1); + foo(2); + }); + taking_multiple_units( + { + foo(0); + foo(1); + }, + { + foo(2); + foo(3); + }, + ); } fn ok() { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index ce9ab2f1271..145b3c62b06 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,34 +1,53 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:24:5 + --> $DIR/unit_arg.rs:23:5 | LL | foo({}); | ^^^^^^^ | = note: `-D clippy::unit-arg` implied by `-D warnings` -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | {}; foo({}); - | ^^^ -help: ...and use unit literals instead +LL | {}; + | +help: ...and use a unit literal instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:25:5 + --> $DIR/unit_arg.rs:24:5 | LL | / foo({ LL | | 1; LL | | }); | |______^ | -help: move the expressions in front of the call... +help: remove the semicolon from the last statement in the block + | +LL | 1 + | +help: or move the expression in front of the call... | LL | { LL | 1; -LL | }; foo({ +LL | }; | -help: ...and use unit literals instead +help: ...and use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:27:5 + | +LL | foo(foo(1)); + | ^^^^^^^^^^^ + | +help: move the expression in front of the call... + | +LL | foo(1); + | +help: ...and use a unit literal instead | LL | foo(()); | ^^ @@ -36,106 +55,157 @@ LL | foo(()); error: passing a unit value to a function --> $DIR/unit_arg.rs:28:5 | -LL | foo(foo(1)); - | ^^^^^^^^^^^ - | -help: move the expressions in front of the call... - | -LL | foo(1); foo(foo(1)); - | ^^^^^^^ -help: ...and use unit literals instead - | -LL | foo(()); - | ^^ - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:29:5 - | LL | / foo({ LL | | foo(1); LL | | foo(2); LL | | }); | |______^ | -help: move the expressions in front of the call... +help: remove the semicolon from the last statement in the block + | +LL | foo(2) + | +help: or move the expression in front of the call... | LL | { LL | foo(1); LL | foo(2); -LL | }; foo({ +LL | }; | -help: ...and use unit literals instead +help: ...and use a unit literal instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo3({}, 2, 2); | ^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | {}; foo3({}, 2, 2); - | ^^^ -help: ...and use unit literals instead +LL | {}; + | +help: ...and use a unit literal instead | LL | foo3((), 2, 2); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:34:5 | LL | / b.bar({ LL | | 1; LL | | }); | |______^ | -help: move the expressions in front of the call... +help: remove the semicolon from the last statement in the block + | +LL | 1 + | +help: or move the expression in front of the call... | LL | { LL | 1; -LL | }; b.bar({ +LL | }; | -help: ...and use unit literals instead +help: ...and use a unit literal instead | LL | b.bar(()); | ^^ -error: passing a unit value to a function - --> $DIR/unit_arg.rs:38:5 +error: passing unit values to a function + --> $DIR/unit_arg.rs:37:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: move the expressions in front of the call... | -LL | foo(0); foo(1); taking_multiple_units(foo(0), foo(1)); - | ^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); + | help: ...and use unit literals instead | -LL | taking_multiple_units((), foo(1)); - | ^^ +LL | taking_multiple_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:38:5 + | +LL | / taking_multiple_units(foo(0), { +LL | | foo(1); +LL | | foo(2); +LL | | }); + | |______^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(2) + | +help: or move the expressions in front of the call... + | +LL | foo(0); +LL | { +LL | foo(1); +LL | foo(2); +LL | }; + | help: ...and use unit literals instead | -LL | taking_multiple_units(foo(0), ()); - | ^^ +LL | taking_multiple_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:42:5 + | +LL | / taking_multiple_units( +LL | | { +LL | | foo(0); +LL | | foo(1); +... | +LL | | }, +LL | | ); + | |_____^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(1) + | +help: remove the semicolon from the last statement in the block + | +LL | foo(3) + | +help: or move the expressions in front of the call... + | +LL | { +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); + ... +help: ...and use unit literals instead + | +LL | (), +LL | (), + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:71:5 + --> $DIR/unit_arg.rs:84:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | foo(1); Some(foo(1)) - | ^^^^^^^ -help: ...and use unit literals instead +LL | foo(1); + | +help: ...and use a unit literal instead | LL | Some(()) | ^^ -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors From a9cde3a804808e82402888a20878053404a8eded Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 18:45:16 +0200 Subject: [PATCH 0141/1110] Don't suggest to move empty blocks --- clippy_lints/src/types.rs | 43 +++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6866635b904..5ca30d598eb 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -10,7 +10,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, + BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, same_tys, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, same_tys, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -847,6 +847,7 @@ fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[ }); let sugg = args_to_recover .iter() + .filter(|arg| !is_empty_block(arg)) .enumerate() .map(|(i, arg)| { let indent = if i == 0 { @@ -860,16 +861,20 @@ fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[ snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) ) }) - .collect::>() - .join("\n"); - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg), - applicability, - ); + .collect::>(); + let mut and = ""; + if !sugg.is_empty() { + let plural = if sugg.len() > 1 { "s" } else { "" }; + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + &format!("{}move the expression{} in front of the call...", or, plural), + format!("{}\n", sugg.join("\n")), + applicability, + ); + and = "...and " + } db.multipart_suggestion( - &format!("...and use {}unit literal{} instead", singular, plural), + &format!("{}use {}unit literal{} instead", and, singular, plural), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -880,6 +885,18 @@ fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[ ); } +fn is_empty_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: &[], expr: None, .. + }, + _, + ) + ) +} + fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::DesugaringKind; if let ExprKind::Call(ref callee, _) = expr.kind { From 77dd0ea62aa6a2af70da4c5e05de064eee182a6c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 19:29:36 +0200 Subject: [PATCH 0142/1110] Add tests for empty blocks --- tests/ui/unit_arg.rs | 2 -- tests/ui/unit_arg.stderr | 46 +++++------------------- tests/ui/unit_arg_empty_blocks.rs | 26 ++++++++++++++ tests/ui/unit_arg_empty_blocks.stderr | 51 +++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 tests/ui/unit_arg_empty_blocks.rs create mode 100644 tests/ui/unit_arg_empty_blocks.stderr diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 7d1b99fedc9..2992abae775 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -20,7 +20,6 @@ impl Bar { } fn bad() { - foo({}); foo({ 1; }); @@ -29,7 +28,6 @@ fn bad() { foo(1); foo(2); }); - foo3({}, 2, 2); let b = Bar; b.bar({ 1; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 145b3c62b06..56f6a855dfa 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,27 +1,12 @@ error: passing a unit value to a function --> $DIR/unit_arg.rs:23:5 | -LL | foo({}); - | ^^^^^^^ - | - = note: `-D clippy::unit-arg` implied by `-D warnings` -help: move the expression in front of the call... - | -LL | {}; - | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:24:5 - | LL | / foo({ LL | | 1; LL | | }); | |______^ | + = note: `-D clippy::unit-arg` implied by `-D warnings` help: remove the semicolon from the last statement in the block | LL | 1 @@ -38,7 +23,7 @@ LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:26:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -53,7 +38,7 @@ LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:28:5 + --> $DIR/unit_arg.rs:27:5 | LL | / foo({ LL | | foo(1); @@ -80,21 +65,6 @@ LL | foo(()); error: passing a unit value to a function --> $DIR/unit_arg.rs:32:5 | -LL | foo3({}, 2, 2); - | ^^^^^^^^^^^^^^ - | -help: move the expression in front of the call... - | -LL | {}; - | -help: ...and use a unit literal instead - | -LL | foo3((), 2, 2); - | ^^ - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 - | LL | / b.bar({ LL | | 1; LL | | }); @@ -116,7 +86,7 @@ LL | b.bar(()); | ^^ error: passing unit values to a function - --> $DIR/unit_arg.rs:37:5 + --> $DIR/unit_arg.rs:35:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +102,7 @@ LL | taking_multiple_units((), ()); | ^^ ^^ error: passing unit values to a function - --> $DIR/unit_arg.rs:38:5 + --> $DIR/unit_arg.rs:36:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -158,7 +128,7 @@ LL | taking_multiple_units((), ()); | ^^ ^^ error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:40:5 | LL | / taking_multiple_units( LL | | { @@ -193,7 +163,7 @@ LL | (), | error: passing a unit value to a function - --> $DIR/unit_arg.rs:84:5 + --> $DIR/unit_arg.rs:82:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ @@ -207,5 +177,5 @@ help: ...and use a unit literal instead LL | Some(()) | ^^ -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs new file mode 100644 index 00000000000..18a31eb3dee --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -0,0 +1,26 @@ +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +fn bad() { + foo({}); + foo3({}, 2, 2); + taking_two_units({}, foo(0)); + taking_three_units({}, foo(0), foo(1)); +} + +fn taking_two_units(a: (), b: ()) {} +fn taking_three_units(a: (), b: (), c: ()) {} + +fn main() { + bad(); +} diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr new file mode 100644 index 00000000000..bb58483584b --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -0,0 +1,51 @@ +error: passing a unit value to a function + --> $DIR/unit_arg_empty_blocks.rs:15:5 + | +LL | foo({}); + | ^^^^--^ + | | + | help: use a unit literal instead: `()` + | + = note: `-D clippy::unit-arg` implied by `-D warnings` + +error: passing a unit value to a function + --> $DIR/unit_arg_empty_blocks.rs:16:5 + | +LL | foo3({}, 2, 2); + | ^^^^^--^^^^^^^ + | | + | help: use a unit literal instead: `()` + +error: passing unit values to a function + --> $DIR/unit_arg_empty_blocks.rs:17:5 + | +LL | taking_two_units({}, foo(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expression in front of the call... + | +LL | foo(0); + | +help: ...and use unit literals instead + | +LL | taking_two_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg_empty_blocks.rs:18:5 + | +LL | taking_three_units({}, foo(0), foo(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(0); +LL | foo(1); + | +help: ...and use unit literals instead + | +LL | taking_three_units((), (), ()); + | ^^ ^^ ^^ + +error: aborting due to 4 previous errors + From 14e9100543166e48acd0ea00233249d2cddf09c2 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 25 May 2020 00:41:13 +0200 Subject: [PATCH 0143/1110] cargo-ui tests: check that /src exists before processing test --- tests/compile-test.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 7bd5f09f333..194354b291f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -181,8 +181,15 @@ fn run_ui_cargo(config: &mut compiletest::Config) { } let src_path = case.path().join("src"); - env::set_current_dir(&src_path)?; + // When switching between branches, if the previous branch had a test + // that the current branch does not have, the directory is not removed + // because an ignored Cargo.lock file exists. + if !src_path.exists() { + continue; + } + + env::set_current_dir(&src_path)?; for file in fs::read_dir(&src_path)? { let file = file?; if file.file_type()?.is_dir() { From 7e843515d9525b6389c3fc1bcfa6ae046c1351dc Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 14 May 2020 15:06:05 -0700 Subject: [PATCH 0144/1110] Created lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++++ clippy_lints/src/sort_by_key_reverse.rs | 28 +++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++++++ tests/ui/sort_by_key_reverse.rs | 5 +++++ 5 files changed, 45 insertions(+) create mode 100644 clippy_lints/src/sort_by_key_reverse.rs create mode 100644 tests/ui/sort_by_key_reverse.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f7dae3dcfff..c00f84bdb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1555,6 +1555,7 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`sort_by_key_reverse`]: https://rust-lang.github.io/rust-clippy/master/index.html#sort_by_key_reverse [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 38cfa212d9f..f51855badff 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,6 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod sort_by_key_reverse; mod strings; mod suspicious_trait_impl; mod swap; @@ -779,6 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &sort_by_key_reverse::SORT_BY_KEY_REVERSE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -1391,6 +1393,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1592,6 +1595,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), + LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs new file mode 100644 index 00000000000..65830afd0f8 --- /dev/null +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -0,0 +1,28 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub SORT_BY_KEY_REVERSE, + complexity, + "default lint description" +} + +declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); + +impl LateLintPass<'_, '_> for SortByKeyReverse {} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 69578732898..1b82f34c863 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1984,6 +1984,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "sort_by_key_reverse", + group: "complexity", + desc: "default lint description", + deprecation: None, + module: "sort_by_key_reverse", + }, Lint { name: "string_add", group: "restriction", diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs new file mode 100644 index 00000000000..2338dc6e594 --- /dev/null +++ b/tests/ui/sort_by_key_reverse.rs @@ -0,0 +1,5 @@ +#![warn(clippy::sort_by_key_reverse)] + +fn main() { + // test code goes here +} From 24847ea53e332853597aca2c7dfe48a9f3be1de8 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 16 May 2020 13:50:33 -0700 Subject: [PATCH 0145/1110] Attempted start at sort_by_key_reverse lint --- clippy_lints/src/sort_by_key_reverse.rs | 71 +++++++++++++++++++++++-- src/lintlist/mod.rs | 2 +- tests/ui/sort_by_key_reverse.fixed | 0 tests/ui/sort_by_key_reverse.rs | 3 +- tests/ui/sort_by_key_reverse.stderr | 0 5 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 tests/ui/sort_by_key_reverse.fixed create mode 100644 tests/ui/sort_by_key_reverse.stderr diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 65830afd0f8..7d7097a8125 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -1,28 +1,91 @@ +use crate::utils::{match_type, span_lint_and_sugg}; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_lint::{LateLintPass, LateContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_hir::*; declare_clippy_lint! { /// **What it does:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// which compares the second argument to the first. /// /// **Why is this bad?** + /// It is more clear to use `Vec::sort_by_key` and `std::cmp::Reverse` /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// // example code where clippy issues a warning + /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// vec.sort_by_key(|e| Reverse(e.foo())); /// ``` pub SORT_BY_KEY_REVERSE, complexity, - "default lint description" + "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" } declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); -impl LateLintPass<'_, '_> for SortByKeyReverse {} +struct LintTrigger { + vec_name: String, + closure_arg: String, + closure_reverse_body: String, + unstable: bool, +} + +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; + if let name = name_ident.ident.name.to_ident_string(); + if name == "sort_by" || name == "sort_unstable_by"; + if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; + if closure_decl.inputs.len() == 2; + if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + then { + let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); + let unstable = name == "sort_unstable_by"; + Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) + } else { + None + } + } +} + +impl LateLintPass<'_, '_> for SortByKeyReverse { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + println!("{:?}", expr); + span_lint_and_sugg( + cx, + SORT_BY_KEY_REVERSE, + expr.span, + "use Vec::sort_by_key here instead", + "try", + String::from("being a better person"), + Applicability::MachineApplicable, + ); + if let Some(trigger) = detect_lint(cx, expr) { + span_lint_and_sugg( + cx, + SORT_BY_KEY_REVERSE, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|{}| Reverse({}))", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_reverse_body, + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b82f34c863..b5d9ef0110e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1987,7 +1987,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "sort_by_key_reverse", group: "complexity", - desc: "default lint description", + desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer", deprecation: None, module: "sort_by_key_reverse", }, diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 2338dc6e594..c0350f243c7 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,5 +1,6 @@ #![warn(clippy::sort_by_key_reverse)] fn main() { - // test code goes here + let mut vec = vec![3, 6, 1, 2, 5]; + vec.sort_by(|a, b| b.cmp(a)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr new file mode 100644 index 00000000000..e69de29bb2d From 8590ab4d46de4eb43e7ebd42cb2f13b0064573e6 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 18 May 2020 21:48:35 -0700 Subject: [PATCH 0146/1110] More progress towards sort_by_key_reverse lint --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/sort_by_key_reverse.rs | 131 ++++++++++++++++++++---- tests/ui/sort_by_key_reverse.fixed | 9 ++ tests/ui/sort_by_key_reverse.rs | 5 +- tests/ui/sort_by_key_reverse.stderr | 22 ++++ 5 files changed, 149 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f51855badff..e7a4c1ecaa9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -998,6 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); + store.register_late_pass(|| box sort_by_key_reverse::SortByKeyReverse); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 7d7097a8125..d70391999a0 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -1,11 +1,12 @@ -use crate::utils::{match_type, span_lint_and_sugg}; +use crate::utils; use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** @@ -40,18 +41,122 @@ struct LintTrigger { unstable: bool, } +/// Detect if the two expressions are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { + match (&a_expr.kind, &b_expr.kind) { + // Two boxes with mirrored contents + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + // Two arrays with mirrored contents + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) + => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are function calls. + // Check to see that the function itself and its arguments are mirrored + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) + => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are method calls. + // Check to see that the function is the same and the arguments are mirrored + // This is enough because the receiver of the method is listed in the arguments + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) + => left_segment.ident == right_segment.ident + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two tuples with mirrored contents + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) + => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two binary ops, which are the same operation and which have mirrored arguments + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) + => left_op.node == right_op.node + && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident), + // Two unary ops, which are the same operation and which have the same argument + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) + => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + // The two exprs are literals of some kind + (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, + (ExprKind::Cast(left_expr, _), ExprKind::Cast(right_expr, _)) + => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::Block(left, _), ExprKind::Block(right, _)) => mirrored_blocks(cx, left, a_ident, right, b_ident), + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) + => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), + // The two exprs are `a` and `b`, directly + (ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: left_ident, .. }], .. },)), + ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: right_ident, .. }], .. },)), + ) => &left_ident == a_ident && &right_ident == b_ident, + // The two exprs are Paths to the same name (which is neither a nor b) + (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), + ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) + => left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) + && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident), + // Matching expressions, but one or both is borrowed + (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) + => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) + => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) + => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + // _ => false, + (left, right) => { + println!("{:?}\n{:?}", left, right); + false + }, + } +} + +/// Detect if the two blocks are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_blocks(cx: &LateContext<'_, '_>, a_block: &Block<'_>, a_ident: &Ident, b_block: &Block<'_>, b_ident: &Ident) -> bool { + match (a_block, b_block) { + (Block { stmts: left_stmts, expr: left_expr, .. }, + Block { stmts: right_stmts, expr: right_expr, .. }) + => left_stmts.iter().zip(right_stmts.iter()).all(|(left, right)| match (&left.kind, &right.kind) { + (StmtKind::Expr(left_expr), StmtKind::Expr(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (StmtKind::Semi(left_expr), StmtKind::Semi(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (StmtKind::Item(left_item), StmtKind::Item(right_item)) => left_item.id == right_item.id, + (StmtKind::Local(left), StmtKind::Local(right)) => mirrored_locals(cx, left, a_ident, right, b_ident), + _ => false, + }) && match (left_expr, right_expr) { + (None, None) => true, + (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + _ => false, + }, + } +} + +/// Check that the two "Local"s (let statements) are equal +fn mirrored_locals(cx: &LateContext<'_, '_>, a_local: &Local<'_>, a_ident: &Ident, b_local: &Local<'_>, b_ident: &Ident) -> bool { + match (a_local, b_local) { + (Local { pat: left_pat, init: left_expr, .. }, Local { pat: right_pat, init: right_expr, .. }) + => match (left_expr, right_expr) { + (None, None) => true, + (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + _ => false, + }, + } +} + fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { if_chain! { if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; - if closure_decl.inputs.len() == 2; - if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; + if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let &[ + Param { pat: Pat { kind: PatKind::Binding(_, _, a_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, b_ident, _), .. }, .. } + ] = &closure_body.params; + if let ExprKind::MethodCall(method_path, _, [ref b_expr, ref a_expr]) = &closure_body.value.kind; + if method_path.ident.name.to_ident_string() == "cmp"; + if mirrored_exprs(&cx, &a_expr, &a_ident, &b_expr, &b_ident); then { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) + let closure_arg = a_ident.name.to_ident_string(); + let closure_reverse_body = Sugg::hir(cx, &a_expr, "..").to_string(); + Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) } else { None } @@ -60,18 +165,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_, '_> for SortByKeyReverse { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - println!("{:?}", expr); - span_lint_and_sugg( - cx, - SORT_BY_KEY_REVERSE, - expr.span, - "use Vec::sort_by_key here instead", - "try", - String::from("being a better person"), - Applicability::MachineApplicable, - ); if let Some(trigger) = detect_lint(cx, expr) { - span_lint_and_sugg( + utils::span_lint_and_sugg( cx, SORT_BY_KEY_REVERSE, expr.span, diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index e69de29bb2d..4b18a073e1a 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::sort_by_key_reverse)] + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + vec.sort_by_key(|a| Reverse(a)); + vec.sort_by_key(|a| Reverse(&(a+5).abs())); + vec.sort_by_key(|a| Reverse(&-a)); +} diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index c0350f243c7..f4fb70b7b1d 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,6 +1,9 @@ +// run-rustfix #![warn(clippy::sort_by_key_reverse)] fn main() { - let mut vec = vec![3, 6, 1, 2, 5]; + let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + vec.sort_by(|a, b| (-b).cmp(&-a)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index e69de29bb2d..36a28c04b1c 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -0,0 +1,22 @@ +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:6:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(a))` + | + = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:7:5 + | +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&(a+5).abs()))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:8:5 + | +LL | vec.sort_by(|a, b| (-b).cmp(&-a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&-a))` + +error: aborting due to 3 previous errors + From 943cb94dce8fca6f3a3f7f011a2a2f9f0a665b97 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 19 May 2020 22:57:27 -0700 Subject: [PATCH 0147/1110] Passes all tests now! --- clippy_lints/src/sort_by_key_reverse.rs | 72 ++++++++----------------- tests/ui/sort_by_key_reverse.fixed | 12 +++-- tests/ui/sort_by_key_reverse.rs | 8 ++- tests/ui/sort_by_key_reverse.stderr | 14 ++--- 4 files changed, 45 insertions(+), 61 deletions(-) diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index d70391999a0..31629a1dbc1 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -53,8 +53,12 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) - => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + => { + // println!("{:?}\n{:?}\n", left_expr, left_args); + // println!("{:?}\n{:?}\n", right_expr, right_args); + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments @@ -74,21 +78,17 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), // The two exprs are literals of some kind (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left_expr, _), ExprKind::Cast(right_expr, _)) + (ExprKind::Cast(left_expr, left_ty), ExprKind::Cast(right_expr, right_ty)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::Block(left, _), ExprKind::Block(right, _)) => mirrored_blocks(cx, left, a_ident, right, b_ident), (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), - // The two exprs are `a` and `b`, directly - (ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: left_ident, .. }], .. },)), - ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: right_ident, .. }], .. },)), - ) => &left_ident == a_ident && &right_ident == b_ident, - // The two exprs are Paths to the same name (which is neither a nor b) + // Two paths: either one is a and the other is b, or they're identical to each other (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) - => left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) - && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident), + => (left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) + && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 && &left_segments[0].ident == a_ident && right_segments.len() == 1 && &right_segments[0].ident == b_ident), // Matching expressions, but one or both is borrowed (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), @@ -96,43 +96,11 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), - // _ => false, - (left, right) => { - println!("{:?}\n{:?}", left, right); - false - }, - } -} - -/// Detect if the two blocks are mirrored (identical, except one -/// contains a and the other replaces it with b) -fn mirrored_blocks(cx: &LateContext<'_, '_>, a_block: &Block<'_>, a_ident: &Ident, b_block: &Block<'_>, b_ident: &Ident) -> bool { - match (a_block, b_block) { - (Block { stmts: left_stmts, expr: left_expr, .. }, - Block { stmts: right_stmts, expr: right_expr, .. }) - => left_stmts.iter().zip(right_stmts.iter()).all(|(left, right)| match (&left.kind, &right.kind) { - (StmtKind::Expr(left_expr), StmtKind::Expr(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (StmtKind::Semi(left_expr), StmtKind::Semi(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (StmtKind::Item(left_item), StmtKind::Item(right_item)) => left_item.id == right_item.id, - (StmtKind::Local(left), StmtKind::Local(right)) => mirrored_locals(cx, left, a_ident, right, b_ident), - _ => false, - }) && match (left_expr, right_expr) { - (None, None) => true, - (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - _ => false, - }, - } -} - -/// Check that the two "Local"s (let statements) are equal -fn mirrored_locals(cx: &LateContext<'_, '_>, a_local: &Local<'_>, a_ident: &Ident, b_local: &Local<'_>, b_ident: &Ident) -> bool { - match (a_local, b_local) { - (Local { pat: left_pat, init: left_expr, .. }, Local { pat: right_pat, init: right_expr, .. }) - => match (left_expr, right_expr) { - (None, None) => true, - (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - _ => false, - }, + _ => false, + // (left, right) => { + // println!("{:?}\n{:?}", left, right); + // false + // }, } } @@ -154,8 +122,12 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option then { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - let closure_arg = a_ident.name.to_ident_string(); - let closure_reverse_body = Sugg::hir(cx, &a_expr, "..").to_string(); + let closure_arg = format!("&{}", b_ident.name.to_ident_string()); + let closure_reverse_body = Sugg::hir(cx, &b_expr, "..").to_string(); + // Get rid of parentheses, because they aren't needed anymore + // while closure_reverse_body.chars().next() == Some('(') && closure_reverse_body.chars().last() == Some(')') { + // closure_reverse_body = String::from(&closure_reverse_body[1..closure_reverse_body.len()-1]); + // } Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) } else { None diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index 4b18a073e1a..d536dc385d5 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -1,9 +1,15 @@ // run-rustfix #![warn(clippy::sort_by_key_reverse)] +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; - vec.sort_by_key(|a| Reverse(a)); - vec.sort_by_key(|a| Reverse(&(a+5).abs())); - vec.sort_by_key(|a| Reverse(&-a)); + vec.sort_by_key(|&b| Reverse(b)); + vec.sort_by_key(|&b| Reverse((b + 5).abs())); + vec.sort_by_key(|&b| Reverse(id(-b))); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index f4fb70b7b1d..9c42d401755 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,9 +1,15 @@ // run-rustfix #![warn(clippy::sort_by_key_reverse)] +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - vec.sort_by(|a, b| (-b).cmp(&-a)); + vec.sort_by(|a, b| id(-b).cmp(&id(-a))); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index 36a28c04b1c..3d26ddae78a 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -1,22 +1,22 @@ error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:6:5 + --> $DIR/sort_by_key_reverse.rs:12:5 | LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(a))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` | = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:7:5 + --> $DIR/sort_by_key_reverse.rs:13:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&(a+5).abs()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:8:5 + --> $DIR/sort_by_key_reverse.rs:14:5 | -LL | vec.sort_by(|a, b| (-b).cmp(&-a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&-a))` +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` error: aborting due to 3 previous errors From 955a25ee7db234a8ab697176a433070702aabe59 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 20 May 2020 09:23:00 -0700 Subject: [PATCH 0148/1110] Added negative test cases and ran cargo dev fmt --- clippy_lints/src/sort_by_key_reverse.rs | 125 ++++++++++++++++-------- tests/ui/sort_by_key_reverse.fixed | 7 ++ tests/ui/sort_by_key_reverse.rs | 9 +- tests/ui/sort_by_key_reverse.stderr | 4 +- 4 files changed, 100 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 31629a1dbc1..ea850955db1 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -43,64 +43,105 @@ struct LintTrigger { /// Detect if the two expressions are mirrored (identical, except one /// contains a and the other replaces it with b) -fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { +fn mirrored_exprs( + cx: &LateContext<'_, '_>, + a_expr: &Expr<'_>, + a_ident: &Ident, + b_expr: &Expr<'_>, + b_ident: &Ident, +) -> bool { match (&a_expr.kind, &b_expr.kind) { // Two boxes with mirrored contents - (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, // Two arrays with mirrored contents - (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) - => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored - (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) - => { - // println!("{:?}\n{:?}\n", left_expr, left_args); - // println!("{:?}\n{:?}\n", right_expr, right_args); - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments - (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) - => left_segment.ident == right_segment.ident - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { + left_segment.ident == right_segment.ident + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // Two tuples with mirrored contents - (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) - => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), // Two binary ops, which are the same operation and which have mirrored arguments - (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) - => left_op.node == right_op.node + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { + left_op.node == right_op.node && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) - && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident), + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) + }, // Two unary ops, which are the same operation and which have the same argument - (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) - => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { + left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, // The two exprs are literals of some kind (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left_expr, left_ty), ExprKind::Cast(right_expr, right_ty)) - => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) - => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), + (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { + mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) + }, + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { + left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) + }, // Two paths: either one is a and the other is b, or they're identical to each other - (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), - ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) - => (left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) - && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) - || (left_segments.len() == 1 && &left_segments[0].ident == a_ident && right_segments.len() == 1 && &right_segments[0].ident == b_ident), + ( + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: left_segments, + .. + }, + )), + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: right_segments, + .. + }, + )), + ) => { + (left_segments + .iter() + .zip(right_segments.iter()) + .all(|(left, right)| left.ident == right.ident) + && left_segments + .iter() + .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 + && &left_segments[0].ident == a_ident + && right_segments.len() == 1 + && &right_segments[0].ident == b_ident) + }, // Matching expressions, but one or both is borrowed - (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) - => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) - => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), - (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) - => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + ( + ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), + ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), + ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { + mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) + }, + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), _ => false, - // (left, right) => { - // println!("{:?}\n{:?}", left, right); - // false - // }, } } diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index d536dc385d5..722675a6b71 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -12,4 +12,11 @@ fn main() { vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); vec.sort_by_key(|&b| Reverse(id(-b))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); + vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 9c42d401755..601621ffa9f 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -10,6 +10,13 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); + vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index 3d26ddae78a..b757c8a6176 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -9,8 +9,8 @@ LL | vec.sort_by(|a, b| b.cmp(a)); error: use Vec::sort_by_key here instead --> $DIR/sort_by_key_reverse.rs:13:5 | -LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead --> $DIR/sort_by_key_reverse.rs:14:5 From 059e8edd15401d5544260e4058731dc8818578d5 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 24 May 2020 19:45:41 -0700 Subject: [PATCH 0149/1110] Detect also a non-reversed comparison --- clippy_lints/src/lib.rs | 10 ++-- ...{sort_by_key_reverse.rs => sort_by_key.rs} | 52 +++++++++++-------- ...by_key_reverse.fixed => sort_by_key.fixed} | 8 ++- ...{sort_by_key_reverse.rs => sort_by_key.rs} | 6 ++- tests/ui/sort_by_key.stderr | 48 +++++++++++++++++ tests/ui/sort_by_key_reverse.stderr | 22 -------- 6 files changed, 94 insertions(+), 52 deletions(-) rename clippy_lints/src/{sort_by_key_reverse.rs => sort_by_key.rs} (83%) rename tests/ui/{sort_by_key_reverse.fixed => sort_by_key.fixed} (72%) rename tests/ui/{sort_by_key_reverse.rs => sort_by_key.rs} (78%) create mode 100644 tests/ui/sort_by_key.stderr delete mode 100644 tests/ui/sort_by_key_reverse.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7a4c1ecaa9..9e826316f21 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod sort_by_key_reverse; +mod sort_by_key; mod strings; mod suspicious_trait_impl; mod swap; @@ -780,7 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &sort_by_key_reverse::SORT_BY_KEY_REVERSE, + &sort_by_key::SORT_BY_KEY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -998,7 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box sort_by_key_reverse::SortByKeyReverse); + store.register_late_pass(|| box sort_by_key::SortByKey); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1394,7 +1394,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), + LintId::of(&sort_by_key::SORT_BY_KEY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1596,7 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), + LintId::of(&sort_by_key::SORT_BY_KEY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key.rs similarity index 83% rename from clippy_lints/src/sort_by_key_reverse.rs rename to clippy_lints/src/sort_by_key.rs index ea850955db1..109845a28f4 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key.rs @@ -11,33 +11,35 @@ use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** /// Detects when people use `Vec::sort_by` and pass in a function - /// which compares the second argument to the first. + /// which compares the two arguments, either directly or indirectly. /// /// **Why is this bad?** - /// It is more clear to use `Vec::sort_by_key` and `std::cmp::Reverse` + /// It is more clear to use `Vec::sort_by_key` (or + /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than + /// using /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); + /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); /// ``` /// Use instead: /// ```rust - /// vec.sort_by_key(|e| Reverse(e.foo())); + /// vec.sort_by_key(|a| a.foo()); /// ``` - pub SORT_BY_KEY_REVERSE, + pub SORT_BY_KEY, complexity, "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" } -declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); +declare_lint_pass!(SortByKey => [SORT_BY_KEY]); struct LintTrigger { vec_name: String, closure_arg: String, - closure_reverse_body: String, + closure_body: String, unstable: bool, } @@ -154,43 +156,49 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ - Param { pat: Pat { kind: PatKind::Binding(_, _, a_ident, _), .. }, ..}, - Param { pat: Pat { kind: PatKind::Binding(_, _, b_ident, _), .. }, .. } + Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } ] = &closure_body.params; - if let ExprKind::MethodCall(method_path, _, [ref b_expr, ref a_expr]) = &closure_body.value.kind; + if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; if method_path.ident.name.to_ident_string() == "cmp"; - if mirrored_exprs(&cx, &a_expr, &a_ident, &b_expr, &b_ident); then { + let (closure_body, closure_arg) = if mirrored_exprs( + &cx, + &left_expr, + &left_ident, + &right_expr, + &right_ident + ) { + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) + } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { + (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) + } else { + return None; + }; let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - let closure_arg = format!("&{}", b_ident.name.to_ident_string()); - let closure_reverse_body = Sugg::hir(cx, &b_expr, "..").to_string(); - // Get rid of parentheses, because they aren't needed anymore - // while closure_reverse_body.chars().next() == Some('(') && closure_reverse_body.chars().last() == Some(')') { - // closure_reverse_body = String::from(&closure_reverse_body[1..closure_reverse_body.len()-1]); - // } - Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) + Some(LintTrigger { vec_name, unstable, closure_arg, closure_body }) } else { None } } } -impl LateLintPass<'_, '_> for SortByKeyReverse { +impl LateLintPass<'_, '_> for SortByKey { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(trigger) = detect_lint(cx, expr) { utils::span_lint_and_sugg( cx, - SORT_BY_KEY_REVERSE, + SORT_BY_KEY, expr.span, "use Vec::sort_by_key here instead", "try", format!( - "{}.sort{}_by_key(|{}| Reverse({}))", + "{}.sort{}_by_key(|&{}| {})", trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, - trigger.closure_reverse_body, + trigger.closure_body, ), Applicability::MachineApplicable, ); diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key.fixed similarity index 72% rename from tests/ui/sort_by_key_reverse.fixed rename to tests/ui/sort_by_key.fixed index 722675a6b71..f6535c8d8f5 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::sort_by_key_reverse)] +#![warn(clippy::sort_by_key)] use std::cmp::Reverse; @@ -9,6 +9,11 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort_by_key(|&a| a); + vec.sort_by_key(|&a| (a + 5).abs()); + vec.sort_by_key(|&a| id(-a)); + // Reverse examples vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); vec.sort_by_key(|&b| Reverse(id(-b))); @@ -18,5 +23,4 @@ fn main() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_by(|a, _| a.cmp(c)); - vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key.rs similarity index 78% rename from tests/ui/sort_by_key_reverse.rs rename to tests/ui/sort_by_key.rs index 601621ffa9f..953c573d406 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key.rs @@ -9,6 +9,11 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort_by(|a, b| a.cmp(b)); + vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + // Reverse examples vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_by(|a, b| id(-b).cmp(&id(-a))); @@ -18,5 +23,4 @@ fn main() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_by(|a, _| a.cmp(c)); - vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr new file mode 100644 index 00000000000..fa6a9a0fb10 --- /dev/null +++ b/tests/ui/sort_by_key.stderr @@ -0,0 +1,48 @@ +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:13:5 + | +LL | vec.sort_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| a)` + | + = note: `-D clippy::sort-by-key` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:14:5 + | +LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:15:5 + | +LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:17:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:18:5 + | +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:19:5 + | +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` + +error: unknown clippy lint: clippy::sort_by_key_reverse + --> $DIR/sort_by_key.rs:2:9 + | +LL | #![warn(clippy::sort_by_key_reverse)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr deleted file mode 100644 index b757c8a6176..00000000000 --- a/tests/ui/sort_by_key_reverse.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:12:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` - | - = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:13:5 - | -LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:14:5 - | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` - -error: aborting due to 3 previous errors - From 07886a97640b89f72b70805f519bd9d42d7d1c4e Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 24 May 2020 20:05:58 -0700 Subject: [PATCH 0150/1110] Detect also when works --- clippy_lints/src/sort_by_key.rs | 49 ++++++++++++++++++++++++++++----- tests/ui/sort_by_key.fixed | 2 +- tests/ui/sort_by_key.stderr | 4 +-- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs index 109845a28f4..f720d14473a 100644 --- a/clippy_lints/src/sort_by_key.rs +++ b/clippy_lints/src/sort_by_key.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -16,7 +16,7 @@ declare_clippy_lint! { /// **Why is this bad?** /// It is more clear to use `Vec::sort_by_key` (or /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than - /// using + /// using /// /// **Known problems:** None. /// @@ -36,7 +36,17 @@ declare_clippy_lint! { declare_lint_pass!(SortByKey => [SORT_BY_KEY]); -struct LintTrigger { +enum LintTrigger { + Sort(SortDetection), + SortByKey(SortByKeyDetection), +} + +struct SortDetection { + vec_name: String, + unstable: bool, +} + +struct SortByKeyDetection { vec_name: String, closure_arg: String, closure_body: String, @@ -177,7 +187,18 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option }; let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - Some(LintTrigger { vec_name, unstable, closure_arg, closure_body }) + if_chain! { + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind; + if left_name == left_ident; + then { + Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } + else { + Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + } + } } else { None } @@ -186,8 +207,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_, '_> for SortByKey { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - if let Some(trigger) = detect_lint(cx, expr) { - utils::span_lint_and_sugg( + match detect_lint(cx, expr) { + Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( cx, SORT_BY_KEY, expr.span, @@ -201,7 +222,21 @@ impl LateLintPass<'_, '_> for SortByKey { trigger.closure_body, ), Applicability::MachineApplicable, - ); + ), + Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + cx, + SORT_BY_KEY, + expr.span, + "use Vec::sort here instead", + "try", + format!( + "{}.sort{}()", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + ), + Applicability::MachineApplicable, + ), + None => {}, } } } diff --git a/tests/ui/sort_by_key.fixed b/tests/ui/sort_by_key.fixed index f6535c8d8f5..bb88df1a56c 100644 --- a/tests/ui/sort_by_key.fixed +++ b/tests/ui/sort_by_key.fixed @@ -10,7 +10,7 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples - vec.sort_by_key(|&a| a); + vec.sort(); vec.sort_by_key(|&a| (a + 5).abs()); vec.sort_by_key(|&a| id(-a)); // Reverse examples diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr index fa6a9a0fb10..291fd5500f7 100644 --- a/tests/ui/sort_by_key.stderr +++ b/tests/ui/sort_by_key.stderr @@ -1,8 +1,8 @@ -error: use Vec::sort_by_key here instead +error: use Vec::sort here instead --> $DIR/sort_by_key.rs:13:5 | LL | vec.sort_by(|a, b| a.cmp(b)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| a)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | = note: `-D clippy::sort-by-key` implied by `-D warnings` From 015ab9f9259d58a48c171276f6e7190528f1a9ad Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 28 May 2020 18:18:25 -0700 Subject: [PATCH 0151/1110] Renamed to --- clippy_lints/src/lib.rs | 10 +++++----- .../{sort_by_key.rs => unnecessary_sort_by.rs} | 18 +++++++++--------- ..._by_key.fixed => unnecessary_sort_by.fixed} | 0 .../{sort_by_key.rs => unnecessary_sort_by.rs} | 0 ...y_key.stderr => unnecessary_sort_by.stderr} | 0 5 files changed, 14 insertions(+), 14 deletions(-) rename clippy_lints/src/{sort_by_key.rs => unnecessary_sort_by.rs} (95%) rename tests/ui/{sort_by_key.fixed => unnecessary_sort_by.fixed} (100%) rename tests/ui/{sort_by_key.rs => unnecessary_sort_by.rs} (100%) rename tests/ui/{sort_by_key.stderr => unnecessary_sort_by.stderr} (100%) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9e826316f21..46df743b5bf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod sort_by_key; +mod unnecessary_sort_by; mod strings; mod suspicious_trait_impl; mod swap; @@ -780,7 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &sort_by_key::SORT_BY_KEY, + &unnecessary_sort_by::UNNECESSARY_SORT_BY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -998,7 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box sort_by_key::SortByKey); + store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1394,7 +1394,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&sort_by_key::SORT_BY_KEY), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1596,7 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&sort_by_key::SORT_BY_KEY), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/unnecessary_sort_by.rs similarity index 95% rename from clippy_lints/src/sort_by_key.rs rename to clippy_lints/src/unnecessary_sort_by.rs index f720d14473a..c0858ec4c88 100644 --- a/clippy_lints/src/sort_by_key.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -14,9 +14,9 @@ declare_clippy_lint! { /// which compares the two arguments, either directly or indirectly. /// /// **Why is this bad?** - /// It is more clear to use `Vec::sort_by_key` (or - /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than - /// using + /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if + /// possible) than to use `Vec::sort_by` and and a more complicated + /// closure. /// /// **Known problems:** None. /// @@ -29,12 +29,12 @@ declare_clippy_lint! { /// ```rust /// vec.sort_by_key(|a| a.foo()); /// ``` - pub SORT_BY_KEY, + pub UNNECESSARY_SORT_BY, complexity, - "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" + "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer" } -declare_lint_pass!(SortByKey => [SORT_BY_KEY]); +declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]); enum LintTrigger { Sort(SortDetection), @@ -205,12 +205,12 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option } } -impl LateLintPass<'_, '_> for SortByKey { +impl LateLintPass<'_, '_> for UnnecessarySortBy { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { match detect_lint(cx, expr) { Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( cx, - SORT_BY_KEY, + UNNECESSARY_SORT_BY, expr.span, "use Vec::sort_by_key here instead", "try", @@ -225,7 +225,7 @@ impl LateLintPass<'_, '_> for SortByKey { ), Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( cx, - SORT_BY_KEY, + UNNECESSARY_SORT_BY, expr.span, "use Vec::sort here instead", "try", diff --git a/tests/ui/sort_by_key.fixed b/tests/ui/unnecessary_sort_by.fixed similarity index 100% rename from tests/ui/sort_by_key.fixed rename to tests/ui/unnecessary_sort_by.fixed diff --git a/tests/ui/sort_by_key.rs b/tests/ui/unnecessary_sort_by.rs similarity index 100% rename from tests/ui/sort_by_key.rs rename to tests/ui/unnecessary_sort_by.rs diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/unnecessary_sort_by.stderr similarity index 100% rename from tests/ui/sort_by_key.stderr rename to tests/ui/unnecessary_sort_by.stderr From 20cb512e81ad03a014b40c377a01fdebaea66963 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 12:06:32 -0700 Subject: [PATCH 0152/1110] Updated test cases and formatted --- clippy_lints/src/lib.rs | 2 +- tests/ui/unnecessary_sort_by.fixed | 1 - tests/ui/unnecessary_sort_by.rs | 1 - tests/ui/unnecessary_sort_by.stderr | 24 ++++++++---------------- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 46df743b5bf..fd832d11577 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,6 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod unnecessary_sort_by; mod strings; mod suspicious_trait_impl; mod swap; @@ -319,6 +318,7 @@ mod try_err; mod types; mod unicode; mod unnamed_address; +mod unnecessary_sort_by; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index bb88df1a56c..4521ae38d49 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::sort_by_key)] use std::cmp::Reverse; diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 953c573d406..fdb5a823369 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::sort_by_key_reverse)] use std::cmp::Reverse; diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 291fd5500f7..b6365c1709d 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,48 +1,40 @@ error: use Vec::sort here instead - --> $DIR/sort_by_key.rs:13:5 + --> $DIR/unnecessary_sort_by.rs:12:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | - = note: `-D clippy::sort-by-key` implied by `-D warnings` + = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:13:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:19:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` -error: unknown clippy lint: clippy::sort_by_key_reverse - --> $DIR/sort_by_key.rs:2:9 - | -LL | #![warn(clippy::sort_by_key_reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` - | - = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors From 32fde0b5116b3a1115d11c49a9bf2af2ebdd5773 Mon Sep 17 00:00:00 2001 From: Ericko Samudera Date: Mon, 25 May 2020 23:22:01 +0700 Subject: [PATCH 0153/1110] New lint: iter_next_slice --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/loops.rs | 26 ++++----- clippy_lints/src/methods/mod.rs | 84 ++++++++++++++++++++++++++- clippy_lints/src/needless_continue.rs | 2 +- src/lintlist/mod.rs | 7 +++ tests/ui/into_iter_on_ref.fixed | 2 + tests/ui/into_iter_on_ref.rs | 2 + tests/ui/into_iter_on_ref.stderr | 8 ++- tests/ui/iter_next_slice.fixed | 24 ++++++++ tests/ui/iter_next_slice.rs | 24 ++++++++ tests/ui/iter_next_slice.stderr | 28 +++++++++ tests/ui/needless_collect.fixed | 2 +- tests/ui/needless_collect.stderr | 16 ++--- 14 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 tests/ui/iter_next_slice.fixed create mode 100644 tests/ui/iter_next_slice.rs create mode 100644 tests/ui/iter_next_slice.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac9057199f..714e25a32ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1401,6 +1401,7 @@ Released 2018-09-13 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 057d39d4c82..7c16dbd8f26 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -669,6 +669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::INTO_ITER_ON_REF, &methods::ITERATOR_STEP_BY_ZERO, &methods::ITER_CLONED_COLLECT, + &methods::ITER_NEXT_SLICE, &methods::ITER_NTH, &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, @@ -1303,6 +1304,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NEXT_SLICE), LintId::of(&methods::ITER_NTH), LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), @@ -1483,6 +1485,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NEXT_SLICE), LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 38a5829b3f7..dbe41823a9c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; +use rustc_span::symbol::Symbol; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; use std::iter::{once, Iterator}; use std::mem; @@ -2381,32 +2381,32 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(collect)); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - ".count()".to_string(), + "count()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(is_empty) { - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(iter)); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - ".next().is_none()".to_string(), + "get(0).is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(collect)); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2422,7 +2422,7 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' span, "replace with", format!( - ".any(|{}| x == {})", + "any(|{}| x == {})", arg, pred ), Applicability::MachineApplicable, @@ -2435,13 +2435,13 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' } } -fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args) = expr.kind; - if let ExprKind::MethodCall(_, ref span, _) = args[0].kind; - then { - return expr.span.with_lo(span.lo() - BytePos(1)); +fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { + let mut current_expr = expr; + while let ExprKind::MethodCall(ref path, ref span, ref args) = current_expr.kind { + if path.ident.name == target_fn_name { + return expr.span.with_lo(span.lo()); } + current_expr = &args[0]; } unreachable!() } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 32b3b7f7947..7cb04d4d81c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,7 +26,7 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, + get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability, @@ -1242,6 +1242,32 @@ declare_clippy_lint! { "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `iter().next()` on a Slice or an Array + /// + /// **Why is this bad?** These can be shortened into `.get()` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a[2..].iter().next(); + /// b.iter().next(); + /// ``` + /// should be written as: + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a.get(2); + /// b.get(0); + /// ``` + pub ITER_NEXT_SLICE, + style, + "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1273,6 +1299,7 @@ declare_lint_pass!(Methods => [ FIND_MAP, MAP_FLATTEN, ITERATOR_STEP_BY_ZERO, + ITER_NEXT_SLICE, ITER_NTH, ITER_NTH_ZERO, ITER_SKIP_NEXT, @@ -1320,6 +1347,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), + ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), @@ -2184,6 +2212,60 @@ fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args } } +fn lint_iter_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { + let caller_expr = &iter_args[0]; + + // Skip lint if the `iter().next()` expression is a for loop argument, + // since it is already covered by `&loops::ITER_NEXT_LOOP` + let mut parent_expr_opt = get_parent_expr(cx, expr); + while let Some(parent_expr) = parent_expr_opt { + if higher::for_loop(parent_expr).is_some() { + return; + } + parent_expr_opt = get_parent_expr(cx, parent_expr); + } + + if derefs_to_slice(cx, caller_expr, cx.tables.expr_ty(caller_expr)).is_some() { + // caller is a Slice + if_chain! { + if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; + if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) + = higher::range(cx, index_expr); + if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; + if let ast::LitKind::Int(start_idx, _) = start_lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on a Slice without end index.", + "try calling", + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + applicability, + ); + } + } + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(caller_expr), sym!(vec_type)) + || matches!(&walk_ptrs_ty(cx.tables.expr_ty(caller_expr)).kind, ty::Array(_, _)) + { + // caller is a Vec or an Array + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on an array", + "try calling", + format!( + "{}.get(0)", + snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) + ), + applicability, + ); + } +} + fn lint_iter_nth<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 28183810df4..a971d041ca6 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -424,7 +424,7 @@ fn erode_from_back(s: &str) -> String { } fn span_of_first_expr_in_block(block: &ast::Block) -> Option { - block.stmts.iter().next().map(|stmt| stmt.span) + block.stmts.get(0).map(|stmt| stmt.span) } #[cfg(test)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8211a57b564..79da1f3702e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -934,6 +934,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "loops", }, + Lint { + name: "iter_next_slice", + group: "style", + desc: "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`", + deprecation: None, + module: "methods", + }, Lint { name: "iter_nth", group: "perf", diff --git a/tests/ui/into_iter_on_ref.fixed b/tests/ui/into_iter_on_ref.fixed index c30d23de3f8..7f92d0dbdc9 100644 --- a/tests/ui/into_iter_on_ref.fixed +++ b/tests/ui/into_iter_on_ref.fixed @@ -40,4 +40,6 @@ fn main() { let _ = (&HashSet::::new()).iter(); //~ WARN equivalent to .iter() let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter() let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter() + + let _ = (&[1, 2, 3]).iter().next(); //~ WARN equivalent to .iter() } diff --git a/tests/ui/into_iter_on_ref.rs b/tests/ui/into_iter_on_ref.rs index 94bc1689619..416056d3fdb 100644 --- a/tests/ui/into_iter_on_ref.rs +++ b/tests/ui/into_iter_on_ref.rs @@ -40,4 +40,6 @@ fn main() { let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() + + let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() } diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 80e2d104f82..1cd6400b019 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -156,5 +156,11 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not move the LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 26 previous errors +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:44:26 + | +LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: aborting due to 27 previous errors diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed new file mode 100644 index 00000000000..79c1db87ac3 --- /dev/null +++ b/tests/ui/iter_next_slice.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.get(0); + // Should be replaced by s.get(0) + + s.get(2); + // Should be replaced by s.get(2) + + v.get(5); + // Should be replaced by v.get(5) + + v.get(0); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs new file mode 100644 index 00000000000..ef9a55f3d99 --- /dev/null +++ b/tests/ui/iter_next_slice.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.iter().next(); + // Should be replaced by s.get(0) + + s[2..].iter().next(); + // Should be replaced by s.get(2) + + v[5..].iter().next(); + // Should be replaced by v.get(5) + + v.iter().next(); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr new file mode 100644 index 00000000000..bbf61df0cda --- /dev/null +++ b/tests/ui/iter_next_slice.stderr @@ -0,0 +1,28 @@ +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:9:5 + | +LL | s.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | + = note: `-D clippy::iter-next-slice` implied by `-D warnings` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:12:5 + | +LL | s[2..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:15:5 + | +LL | v[5..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` + +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:18:5 + | +LL | v.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index b4227eaf2f8..be37dc16b9a 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -9,7 +9,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.iter().next().is_none() { + if sample.get(0).is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 8884c8e1612..9113aad90dd 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,28 +1,28 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:11:28 + --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` | = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:21 + --> $DIR/needless_collect.rs:12:15 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:15:27 + --> $DIR/needless_collect.rs:15:28 | LL | sample.iter().cloned().collect::>().contains(&1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:16:34 + --> $DIR/needless_collect.rs:16:35 | LL | sample.iter().map(|x| (x, x)).collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: aborting due to 4 previous errors From 7727c4ac7f3d4c977866bd8e6659a3e27f0bb6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 28 May 2020 21:42:01 +0200 Subject: [PATCH 0154/1110] CONTRIBUTING: explain how to use cargo dev ra-setup Fixes #5514 --- CONTRIBUTING.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f47ac98fd2..9f7bdcb1be7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,14 +12,16 @@ anything, feel free to ask questions on issues or visit the `#clippy` on [Discor All contributors are expected to follow the [Rust Code of Conduct]. -* [Getting started](#getting-started) - * [Finding something to fix/improve](#finding-something-to-fiximprove) -* [Writing code](#writing-code) -* [How Clippy works](#how-clippy-works) -* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust) -* [Issue and PR Triage](#issue-and-pr-triage) -* [Bors and Homu](#bors-and-homu) -* [Contributions](#contributions) +- [Contributing to Clippy](#contributing-to-clippy) + - [Getting started](#getting-started) + - [Finding something to fix/improve](#finding-something-to-fiximprove) + - [Writing code](#writing-code) + - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) + - [How Clippy works](#how-clippy-works) + - [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust) + - [Issue and PR triage](#issue-and-pr-triage) + - [Bors and Homu](#bors-and-homu) + - [Contributions](#contributions) [Discord]: https://discord.gg/rust-lang [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct @@ -91,6 +93,24 @@ quick read. [rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees [rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories +## Getting code-completion for rustc internals to work + +Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not +available via a `rustup` component at the time of writing. +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via +`git clone https://github.com/rust-lang/rust/`. +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies +which rust-analyzer will be able to understand. +Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo +you just cloned. +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to +Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. +Just make sure to remove the dependencies again before finally making a pull request! + +[ra_homepage]: https://rust-analyzer.github.io/ +[rustc_repo]: https://github.com/rust-lang/rust/ + ## How Clippy works [`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`]. From 9a5baed482b68e0d9806e19eb9e8676d7ff3e1c2 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:09:12 -0700 Subject: [PATCH 0155/1110] Implement suggestions from phansch --- clippy_lints/src/unnecessary_sort_by.rs | 41 ++++++++++++++++++++----- tests/ui/unnecessary_sort_by.fixed | 7 +++-- tests/ui/unnecessary_sort_by.rs | 7 +++-- tests/ui/unnecessary_sort_by.stderr | 26 ++++++++++------ 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index c0858ec4c88..33d8331c292 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -18,15 +18,25 @@ declare_clippy_lint! { /// possible) than to use `Vec::sort_by` and and a more complicated /// closure. /// - /// **Known problems:** None. + /// **Known problems:** + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't + /// imported by a use statement in the current frame, then a `use` + /// statement that imports it will need to be added (which this lint + /// can't do). /// /// **Example:** /// /// ```rust - /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); + /// vec.sort_by(|a, b| a.foo().cmp(&b.foo())); /// ``` /// Use instead: /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); /// vec.sort_by_key(|a| a.foo()); /// ``` pub UNNECESSARY_SORT_BY, @@ -50,6 +60,7 @@ struct SortByKeyDetection { vec_name: String, closure_arg: String, closure_body: String, + reverse: bool, unstable: bool, } @@ -172,16 +183,16 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; if method_path.ident.name.to_ident_string() == "cmp"; then { - let (closure_body, closure_arg) = if mirrored_exprs( + let (closure_body, closure_arg, reverse) = if mirrored_exprs( &cx, &left_expr, &left_ident, &right_expr, &right_ident ) { - (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false) } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { - (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) + (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) } else { return None; }; @@ -196,7 +207,13 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) } else { - Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) } } } else { @@ -219,9 +236,17 @@ impl LateLintPass<'_, '_> for UnnecessarySortBy { trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, - trigger.closure_body, + if trigger.reverse { + format!("Reverse({})", trigger.closure_body) + } else { + trigger.closure_body.to_string() + }, ), - Applicability::MachineApplicable, + if trigger.reverse { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, ), Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( cx, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 4521ae38d49..779fd57707a 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -10,16 +10,17 @@ fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort(); + vec.sort_unstable(); vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_by_key(|&a| id(-a)); + vec.sort_unstable_by_key(|&a| id(-a)); // Reverse examples vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_by_key(|&b| Reverse(id(-b))); + vec.sort_unstable_by_key(|&b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index fdb5a823369..0485a5630af 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -10,16 +10,17 @@ fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort_by(|a, b| a.cmp(b)); + vec.sort_unstable_by(|a, b| a.cmp(b)); vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); } diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index b6365c1709d..903b6e5099c 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -6,35 +6,41 @@ LL | vec.sort_by(|a, b| a.cmp(b)); | = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` -error: use Vec::sort_by_key here instead +error: use Vec::sort here instead --> $DIR/unnecessary_sort_by.rs:13:5 | +LL | vec.sort_unstable_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:14:5 + | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | -LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` +LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:16:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` +LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors From b89880a30ce4dd7887614f305a565b6779dc4825 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:19:31 -0700 Subject: [PATCH 0156/1110] Ran update_lints --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +++--- src/lintlist/mod.rs | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c00f84bdb85..086a1141be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1555,7 +1555,6 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization -[`sort_by_key_reverse`]: https://rust-lang.github.io/rust-clippy/master/index.html#sort_by_key_reverse [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -1602,6 +1601,7 @@ Released 2018-09-13 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold [`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 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fd832d11577..03addf1f4a4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -780,7 +780,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &unnecessary_sort_by::UNNECESSARY_SORT_BY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -835,6 +834,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unicode::ZERO_WIDTH_SPACE, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, + &unnecessary_sort_by::UNNECESSARY_SORT_BY, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, @@ -1394,7 +1394,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1431,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unicode::ZERO_WIDTH_SPACE), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1596,7 +1596,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), @@ -1613,6 +1612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_ARG), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b5d9ef0110e..ab9542a7b9c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1984,13 +1984,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, - Lint { - name: "sort_by_key_reverse", - group: "complexity", - desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer", - deprecation: None, - module: "sort_by_key_reverse", - }, Lint { name: "string_add", group: "restriction", @@ -2299,6 +2292,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "no_effect", }, + Lint { + name: "unnecessary_sort_by", + group: "complexity", + desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer", + deprecation: None, + module: "unnecessary_sort_by", + }, Lint { name: "unnecessary_unwrap", group: "complexity", From 6955420ace822ec888cc999a623c67c51ced839f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 1 Jun 2020 00:28:58 +0200 Subject: [PATCH 0157/1110] Update changelog for stable:1.44 beta:1.45 --- CHANGELOG.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7dae3dcfff..fcc9895dd90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,87 @@ document. ## Unreleased / In Rust Nightly -[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master) +[7ea7cd1...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) + +## Rust 1.45 + +Current beta, release 2020-07-16 + +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1) + +### New lints + +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582) +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493) +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332) +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506) +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439) +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522) +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576) +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583) +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550) + +### Moves and Deprecations + +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408) +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622) +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599) +* Generalize [`option_and_then_some`] and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529) +* Rename [`identity_conversion`] to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568) +* Merge [`block_in_if_condition_expr`] and [`block_in_if_condition_stmt`] into [`blocks_in_if_conditions`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge [`option_map_unwrap_or`], [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] into [`map_unwrap_or`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge [`option_unwrap_used`] and [`result_unwrap_used`] into [`unwrap_used`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge [`option_expect_used`] and [`result_expect_used`] into [`expect_used`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge [`for_loop_over_option`] and [`for_loop_over_result`] into [`for_loops_over_fallibles`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) + +### Enhancements + +* Avoid running cargo/internal lints when not enabled. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505) +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631) +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592) +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616) + +### False Positive Fixes + +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525) +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609) +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558) +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596) +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535) +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491) +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602) +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564) +* Add ignores to the list of words of [`clippy::doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611) +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636) +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647) +* Honor lint level attributes for [`redundant_field_names`] and [`non_expressive_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651) +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429) + +### Suggestion Improvements + +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536) +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511) +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530) +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547) + +### ICE Fixes + +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590) +* Fix crash on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499) + +### Documentation + +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639) +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541) ## Rust 1.44 -Current beta, release 2020-06-04 +Current stable, released 2020-06-04 [204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8) @@ -93,7 +169,7 @@ Current beta, release 2020-06-04 ## Rust 1.43 -Current stable, released 2020-04-23 +Released 2020-04-23 [4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b) From ae0ce2255aea7e896cbfc0330c9d4f17ed66b55f Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 1 Jun 2020 09:58:42 +0200 Subject: [PATCH 0158/1110] Add regression test for string_lit_as_bytes issue --- tests/ui/string_lit_as_bytes.fixed | 2 ++ tests/ui/string_lit_as_bytes.rs | 2 ++ tests/ui/string_lit_as_bytes.stderr | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index 7ad272ade5f..ccf8f61c4a9 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -14,6 +14,8 @@ fn str_lit_as_bytes() { let strify = stringify!(foobar).as_bytes(); + let current_version = env!("CARGO_PKG_VERSION").as_bytes(); + let includestr = include_bytes!("entry_unfixable.rs"); let _ = b"string with newline\t\n"; diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index 1bf4538b7c9..178df08e249 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -14,6 +14,8 @@ fn str_lit_as_bytes() { let strify = stringify!(foobar).as_bytes(); + let current_version = env!("CARGO_PKG_VERSION").as_bytes(); + let includestr = include_str!("entry_unfixable.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); diff --git a/tests/ui/string_lit_as_bytes.stderr b/tests/ui/string_lit_as_bytes.stderr index ff6e3346dfc..99c512354d5 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -13,13 +13,13 @@ LL | let bs = r###"raw string with 3# plus " ""###.as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###` error: calling `as_bytes()` on `include_str!(..)` - --> $DIR/string_lit_as_bytes.rs:17:22 + --> $DIR/string_lit_as_bytes.rs:19:22 | LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` error: calling `as_bytes()` on a string literal - --> $DIR/string_lit_as_bytes.rs:19:13 + --> $DIR/string_lit_as_bytes.rs:21:13 | LL | let _ = "string with newline/t/n".as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` From 861b897c54200becd6767ad6e091abef61f15344 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 1 Jun 2020 10:20:17 +0200 Subject: [PATCH 0159/1110] Add regression test for endless loop This was fixed in pulldown_cmark 0.7.1, specifically https://github.com/raphlinus/pulldown-cmark/pull/438 --- clippy_lints/Cargo.toml | 2 +- tests/ui/crashes/regressions.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 98391732d18..e959c1a6511 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -21,7 +21,7 @@ cargo_metadata = "0.9.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" -pulldown-cmark = { version = "0.7", default-features = false } +pulldown-cmark = { version = "0.7.1", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 623ae51f9f0..3d5063d1a3a 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -6,4 +6,8 @@ pub fn foo(bar: *const u8) { println!("{:#p}", bar); } +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917 +/// Date: Wed, 27 May 2020 16:24:53 +0200 Subject: [PATCH 0160/1110] Fix some code examples in doc --- clippy_lints/src/assign_ops.rs | 4 +++ clippy_lints/src/double_parens.rs | 18 +++++++++++-- clippy_lints/src/drop_bounds.rs | 4 +++ clippy_lints/src/duration_subsec.rs | 6 +++++ clippy_lints/src/enum_variants.rs | 32 +++++++++++++++++++---- clippy_lints/src/eq_op.rs | 4 +++ clippy_lints/src/escape.rs | 7 +++++ clippy_lints/src/eta_reduction.rs | 4 +++ clippy_lints/src/eval_order_dependence.rs | 6 +++++ 9 files changed, 78 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 05e2650d0b7..13e61fe98ba 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -24,7 +24,11 @@ declare_clippy_lint! { /// let mut a = 5; /// let b = 0; /// // ... + /// // Bad /// a = a + b; + /// + /// // Good + /// a += b; /// ``` pub ASSIGN_OP_PATTERN, style, diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 7f2ff8b9b26..05517f6f9f0 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -13,10 +13,24 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad + /// fn simple_double_parens() -> i32 { + /// ((0)) + /// } + /// + /// // Good + /// fn simple_no_parens() -> i32 { + /// 0 + /// } + /// + /// // or + /// /// # fn foo(bar: usize) {} - /// ((0)); + /// // Bad /// foo((0)); - /// ((1, 2)); + /// + /// // Good + /// foo(0); /// ``` pub DOUBLE_PARENS, complexity, diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index f4966808279..4ef963ac314 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -27,6 +27,10 @@ declare_clippy_lint! { /// ```rust /// fn foo() {} /// ``` + /// Could be written as: + /// ```rust + /// fn foo() {} + /// ``` pub DROP_BOUNDS, correctness, "Bounds of the form `T: Drop` are useless" diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index b35a8facf8b..afefa250638 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -22,8 +22,14 @@ declare_clippy_lint! { /// ```rust /// # use std::time::Duration; /// let dur = Duration::new(5, 0); + /// + /// // Bad /// let _micros = dur.subsec_nanos() / 1_000; /// let _millis = dur.subsec_nanos() / 1_000_000; + /// + /// // Good + /// let _micros = dur.subsec_micros(); + /// let _millis = dur.subsec_millis(); /// ``` pub DURATION_SUBSEC, complexity, diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index a5871cf0cd4..cb0fd59a2d4 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -25,31 +25,47 @@ declare_clippy_lint! { /// BattenbergCake, /// } /// ``` + /// Could be written as: + /// ```rust + /// enum Cake { + /// BlackForest, + /// Hummingbird, + /// Battenberg, + /// } + /// ``` pub ENUM_VARIANT_NAMES, style, "enums where all variants share a prefix/postfix" } declare_clippy_lint! { - /// **What it does:** Detects enumeration variants that are prefixed or suffixed - /// by the same characters. + /// **What it does:** Detects public enumeration variants that are + /// prefixed or suffixed by the same characters. /// - /// **Why is this bad?** Enumeration variant names should specify their variant, + /// **Why is this bad?** Public enumeration variant names should specify their variant, /// not repeat the enumeration name. /// /// **Known problems:** None. /// /// **Example:** /// ```rust - /// enum Cake { + /// pub enum Cake { /// BlackForestCake, /// HummingbirdCake, /// BattenbergCake, /// } /// ``` + /// Could be written as: + /// ```rust + /// pub enum Cake { + /// BlackForest, + /// Hummingbird, + /// Battenberg, + /// } + /// ``` pub PUB_ENUM_VARIANT_NAMES, pedantic, - "enums where all variants share a prefix/postfix" + "public enums where all variants share a prefix/postfix" } declare_clippy_lint! { @@ -66,6 +82,12 @@ declare_clippy_lint! { /// struct BlackForestCake; /// } /// ``` + /// Could be written as: + /// ```rust + /// mod cake { + /// struct BlackForest; + /// } + /// ``` pub MODULE_NAME_REPETITIONS, pedantic, "type names prefixed/postfixed with their containing module's name" diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 4e1c1f13140..d7819d737ea 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -39,7 +39,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// &x == y + /// + /// // Good + /// x == *y /// ``` pub OP_REF, style, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 1ec60a0e6e6..7227683aa5a 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -28,9 +28,16 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # fn foo(bar: usize) {} + /// + /// // Bad /// let x = Box::new(1); /// foo(*x); /// println!("{}", *x); + /// + /// // Good + /// let x = 1; + /// foo(x); + /// println!("{}", x); /// ``` pub BOXED_LOCAL, perf, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index e3e1136b676..5f0cd1ec72c 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -26,7 +26,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore + /// // Bad /// xs.map(|x| foo(x)) + /// + /// // Good + /// foo(xs) /// ``` /// where `foo(_)` is a plain function that takes the exact argument type of /// `x`. diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 5206266ccf2..37e24ff34f7 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -21,11 +21,17 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let mut x = 0; + /// + /// // Bad /// let a = { /// x = 1; /// 1 /// } + x; /// // Unclear whether a is 1 or 2. + /// + /// // Good + /// x = 1; + /// let a = 1 + x; /// ``` pub EVAL_ORDER_DEPENDENCE, complexity, From 262c9dc025042646610df879dd9708eea625534d Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 29 May 2020 18:15:42 +0200 Subject: [PATCH 0161/1110] Fix more code examples --- clippy_lints/src/fallible_impl_from.rs | 15 +++++++++++++-- clippy_lints/src/floating_point_arithmetic.rs | 2 -- clippy_lints/src/format.rs | 6 +++++- clippy_lints/src/functions.rs | 14 ++++++++++---- clippy_lints/src/implicit_saturating_sub.rs | 7 ------- clippy_lints/src/int_plus_one.rs | 1 - clippy_lints/src/integer_division.rs | 11 +++++++---- clippy_lints/src/items_after_statements.rs | 16 ++++++++++++++++ 8 files changed, 51 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 17639cc2a06..575462f25e6 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -18,13 +18,24 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust + /// ```rust,ignore /// struct Foo(i32); + /// + /// // Bad /// impl From for Foo { /// fn from(s: String) -> Self { /// Foo(s.parse().unwrap()) /// } /// } + /// + /// // Good + /// use std::convert::TryFrom; + /// impl TryFrom for Foo { + /// type Error = (); + /// fn try_from(s: String) -> Result { + /// s.parse() + /// } + /// } /// ``` pub FALLIBLE_IMPL_FROM, nursery, @@ -120,7 +131,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it move |diag| { diag.help( "`From` is intended for infallible conversions only. \ - Use `TryFrom` if there's a possibility for the conversion to fail."); + Use `TryFrom` if there's a possibility for the conversion to fail."); diag.span_note(fpu.result, "potential failure(s)"); }); } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 86317fb8bd5..3a912d92837 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -28,7 +28,6 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// /// let a = 3f32; /// let _ = a.powf(1.0 / 3.0); /// let _ = (1.0 + a).ln(); @@ -38,7 +37,6 @@ declare_clippy_lint! { /// is better expressed as /// /// ```rust - /// /// let a = 3f32; /// let _ = a.cbrt(); /// let _ = a.ln_1p(); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 5b092526ce4..1530538aa7d 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -25,9 +25,13 @@ declare_clippy_lint! { /// /// **Examples:** /// ```rust + /// + /// // Bad /// # let foo = "foo"; - /// format!("foo"); /// format!("{}", foo); + /// + /// // Good + /// format!("foo"); /// ``` pub USELESS_FORMAT, complexity, diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index c24a24733d7..9b5f1dee7f4 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -49,11 +49,11 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ``` rust + /// ```rust /// fn im_too_long() { - /// println!(""); + /// println!(""); /// // ... 100 more LoC - /// println!(""); + /// println!(""); /// } /// ``` pub TOO_MANY_LINES, @@ -79,10 +79,16 @@ declare_clippy_lint! { /// `some_argument.get_raw_ptr()`). /// /// **Example:** - /// ```rust + /// ```rust,ignore + /// // Bad /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // Good + /// pub unsafe fn foo(x: *const u8) { + /// println!("{}", unsafe { *x }); + /// } /// ``` pub NOT_UNSAFE_PTR_ARG_DEREF, correctness, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 155a93de4fa..fdaf37e5e08 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -25,13 +25,6 @@ declare_clippy_lint! { /// if i != 0 { /// i -= 1; /// } - /// ``` - /// Use instead: - /// ```rust - /// let end: u32 = 10; - /// let start: u32 = 5; - /// - /// let mut i: u32 = end - start; /// /// // Good /// i = i.saturating_sub(1); diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index d5dbd495680..e91fb0c2f27 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -10,7 +10,6 @@ use crate::utils::{snippet_opt, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block /// - /// /// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`. /// /// **Known problems:** None. diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index fe34d33fe65..d537ef3f323 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -15,10 +15,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// fn main() { - /// let x = 3 / 2; - /// println!("{}", x); - /// } + /// // Bad + /// let x = 3 / 2; + /// println!("{}", x); + /// + /// // Good + /// let x = 3f32 / 2f32; + /// println!("{}", x); /// ``` pub INTEGER_DIVISION, restriction, diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index e7062b7c16b..c8576bcfcb4 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -16,6 +16,7 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// fn foo() { /// println!("cake"); /// } @@ -28,6 +29,21 @@ declare_clippy_lint! { /// foo(); // prints "foo" /// } /// ``` + /// + /// ```rust + /// // Good + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "foo" + /// foo(); // prints "foo" + /// } + /// ``` pub ITEMS_AFTER_STATEMENTS, pedantic, "blocks where an item comes after a statement" From 19339334cb4e9c6db5a1f7dced38edcb16707bc7 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 May 2020 11:38:48 +0200 Subject: [PATCH 0162/1110] Give more corrected code examples in doc --- clippy_lints/src/literal_representation.rs | 12 +++++ clippy_lints/src/matches.rs | 44 ++++++++++++++++--- clippy_lints/src/misc.rs | 31 ++++++++++++- clippy_lints/src/misc_early.rs | 34 +++++++++++--- clippy_lints/src/mut_reference.rs | 4 ++ clippy_lints/src/mutex_atomic.rs | 12 ++++- clippy_lints/src/needless_bool.rs | 3 +- clippy_lints/src/needless_borrow.rs | 8 +++- clippy_lints/src/needless_pass_by_value.rs | 3 +- clippy_lints/src/needless_update.rs | 10 +++++ clippy_lints/src/ptr.rs | 23 ++++++---- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/reference.rs | 5 +++ clippy_lints/src/regex.rs | 12 ++--- clippy_lints/src/shadow.rs | 10 +++++ .../src/single_component_path_imports.rs | 4 +- .../src/slow_vector_initialization.rs | 8 +++- clippy_lints/src/strings.rs | 8 ++++ 18 files changed, 195 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index ec7c4531ed7..7ba43562d7d 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -24,7 +24,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let x: u64 = 61864918973511; + /// + /// // Good + /// let x: u64 = 61_864_918_973_511; /// ``` pub UNREADABLE_LITERAL, pedantic, @@ -44,7 +48,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Probably mistyped /// 2_32; + /// + /// // Good + /// 2_i32; /// ``` pub MISTYPED_LITERAL_SUFFIXES, correctness, @@ -63,7 +71,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let x: u64 = 618_64_9189_73_511; + /// + /// // Good + /// let x: u64 = 61_864_918_973_511; /// ``` pub INCONSISTENT_DIGIT_GROUPING, style, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 94380acfcfd..146212cb2c7 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -36,10 +36,17 @@ declare_clippy_lint! { /// ```rust /// # fn bar(stool: &str) {} /// # let x = Some("abc"); + /// + /// // Bad /// match x { /// Some(ref foo) => bar(foo), /// _ => (), /// } + /// + /// // Good + /// if let Some(ref foo) = x { + /// bar(foo); + /// } /// ``` pub SINGLE_MATCH, style, @@ -97,11 +104,19 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore + /// // Bad /// match x { /// &A(ref y) => foo(y), /// &B => bar(), /// _ => frob(&x), /// } + /// + /// // Good + /// match *x { + /// A(ref y) => foo(y), + /// B => bar(), + /// _ => frob(x), + /// } /// ``` pub MATCH_REF_PATS, style, @@ -197,10 +212,15 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let x: Option<()> = None; + /// + /// // Bad /// let r: Option<&()> = match x { /// None => None, /// Some(ref v) => Some(v), /// }; + /// + /// // Good + /// let r: Option<&()> = x.as_ref(); /// ``` pub MATCH_AS_REF, complexity, @@ -219,10 +239,18 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); + /// + /// // Bad /// match x { /// Foo::A(_) => {}, /// _ => {}, /// } + /// + /// // Good + /// match x { + /// Foo::A(_) => {}, + /// Foo::B(_) => {}, + /// } /// ``` pub WILDCARD_ENUM_MATCH_ARM, restriction, @@ -242,16 +270,15 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A, B, C } /// # let x = Foo::B; + /// + /// // Bad /// match x { /// Foo::A => {}, /// Foo::B => {}, /// _ => {}, /// } - /// ``` - /// Use instead: - /// ```rust - /// # enum Foo { A, B, C } - /// # let x = Foo::B; + /// + /// // Good /// match x { /// Foo::A => {}, /// Foo::B => {}, @@ -273,10 +300,17 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// match "foo" { /// "a" => {}, /// "bar" | _ => {}, /// } + /// + /// // Good + /// match "foo" { + /// "a" => {}, + /// _ => {}, + /// } /// ``` pub WILDCARD_IN_OR_PATTERNS, complexity, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index e1d524c2231..a3b7134a376 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -38,10 +38,16 @@ declare_clippy_lint! { /// dereferences, e.g., changing `*x` to `x` within the function. /// /// **Example:** - /// ```rust + /// ```rust,ignore + /// // Bad /// fn foo(ref x: u8) -> bool { /// true /// } + /// + /// // Good + /// fn foo(x: &u8) -> bool { + /// true + /// } /// ``` pub TOPLEVEL_REF_ARG, style, @@ -60,7 +66,11 @@ declare_clippy_lint! { /// ```rust /// # let x = 1.0; /// + /// // Bad /// if x == f32::NAN { } + /// + /// // Good + /// if x.is_nan() { } /// ``` pub CMP_NAN, correctness, @@ -83,8 +93,15 @@ declare_clippy_lint! { /// ```rust /// let x = 1.2331f64; /// let y = 1.2332f64; + /// + /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats + /// + /// // Good + /// let error = 0.01f64; // Use an epsilon for comparison + /// if (y - 1.23f64).abs() < error { } + /// if (y - x).abs() > error { } /// ``` pub FLOAT_CMP, correctness, @@ -191,7 +208,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let a = 0 as *const u32; + /// + /// // Good + /// let a = std::ptr::null::(); /// ``` pub ZERO_PTR, style, @@ -214,7 +235,13 @@ declare_clippy_lint! { /// ```rust /// let x: f64 = 1.0; /// const ONE: f64 = 1.00; - /// x == ONE; // where both are floats + /// + /// // Bad + /// if x == ONE { } // where both are floats + /// + /// // Good + /// let error = 0.1f64; // Use an epsilon for comparison + /// if (x - ONE).abs() < error { } /// ``` pub FLOAT_CMP_CONST, restriction, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 552222eba2e..ad39e59d067 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -59,7 +59,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// fn foo(a: i32, _a: i32) {} + /// + /// // Good + /// fn bar(a: i32, _b: i32) {} /// ``` pub DUPLICATE_UNDERSCORE_ARGUMENT, style, @@ -77,7 +81,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore - /// (|| 42)() + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 /// ``` pub REDUNDANT_CLOSURE_CALL, complexity, @@ -112,7 +120,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// let y = 0x1a9BAcD; + /// + /// // Good + /// let y = 0x1A9BACD; /// ``` pub MIXED_CASE_HEX_LITERALS, style, @@ -129,7 +141,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// let y = 123832i32; + /// + /// // Good + /// let y = 123832_i32; /// ``` pub UNSEPARATED_LITERAL_SUFFIX, pedantic, @@ -207,9 +223,16 @@ declare_clippy_lint! { /// ```rust /// # let v = Some("abc"); /// + /// // Bad /// match v { /// Some(x) => (), - /// y @ _ => (), // easier written as `y`, + /// y @ _ => (), + /// } + /// + /// // Good + /// match v { + /// Some(x) => (), + /// y => (), /// } /// ``` pub REDUNDANT_PATTERN, @@ -235,16 +258,13 @@ declare_clippy_lint! { /// # struct TupleStruct(u32, u32, u32); /// # let t = TupleStruct(1, 2, 3); /// + /// // Bad /// match t { /// TupleStruct(0, .., _) => (), /// _ => (), /// } - /// ``` - /// can be written as - /// ```rust - /// # struct TupleStruct(u32, u32, u32); - /// # let t = TupleStruct(1, 2, 3); /// + /// // Good /// match t { /// TupleStruct(0, ..) => (), /// _ => (), diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 67a1ac78a67..58a8e1a1064 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -16,7 +16,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// my_vec.push(&mut value) + /// + /// // Good + /// my_vec.push(&value) /// ``` pub UNNECESSARY_MUT_PASSED, style, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 4e1a8be4892..78b15afc5a7 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -22,9 +22,15 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// # let y = true; + /// + /// // Bad /// # use std::sync::Mutex; - /// # let y = 1; /// let x = Mutex::new(&y); + /// + /// // Good + /// # use std::sync::atomic::AtomicBool; + /// let x = AtomicBool::new(y); /// ``` pub MUTEX_ATOMIC, perf, @@ -46,6 +52,10 @@ declare_clippy_lint! { /// ```rust /// # use std::sync::Mutex; /// let x = Mutex::new(0usize); + /// + /// // Good + /// # use std::sync::atomic::AtomicUsize; + /// let x = AtomicUsize::new(0usize); /// ``` pub MUTEX_INTEGER, nursery, diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index efa77db822d..15b129fa098 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -15,8 +15,7 @@ use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for expressions of the form `if c { true } else { - /// false }` - /// (or vice versa) and suggest using the condition directly. + /// false }` (or vice versa) and suggests using the condition directly. /// /// **Why is this bad?** Redundant code. /// diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 9ee875d7516..5880d1d6102 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -18,12 +18,16 @@ declare_clippy_lint! { /// **Why is this bad?** Suggests that the receiver of the expression borrows /// the expression. /// + /// **Known problems:** None. + /// /// **Example:** /// ```rust + /// // Bad /// let x: &i32 = &&&&&&5; - /// ``` /// - /// **Known problems:** None. + /// // Good + /// let x: &i32 = &5; + /// ``` pub NEEDLESS_BORROW, nursery, "taking a reference that is going to be automatically dereferenced" diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 9c508fc0e4a..fbdf927b824 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -40,9 +40,8 @@ declare_clippy_lint! { /// assert_eq!(v.len(), 42); /// } /// ``` - /// + /// should be /// ```rust - /// // should be /// fn foo(v: &[i32]) { /// assert_eq!(v.len(), 42); /// } diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 4b2586877e5..d866bab2f64 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -21,6 +21,16 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; + /// + /// // Bad + /// Point { + /// x: 1, + /// y: 1, + /// z: 1, + /// ..zero_point + /// }; + /// + /// // Ok /// Point { /// x: 1, /// y: 1, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 4eac571f966..c77b44e0c99 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -47,7 +47,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// fn foo(&Vec) { .. } + /// + /// // Good + /// fn foo(&[u32]) { .. } /// ``` pub PTR_ARG, style, @@ -65,9 +69,15 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// if x == ptr::null { /// .. /// } + /// + /// // Good + /// if x.is_null() { + /// .. + /// } /// ``` pub CMP_NULL, style, @@ -76,19 +86,16 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** This lint checks for functions that take immutable - /// references and return - /// mutable ones. + /// references and return mutable ones. /// /// **Why is this bad?** This is trivially unsound, as one can create two - /// mutable references - /// from the same (immutable!) source. This - /// [error](https://github.com/rust-lang/rust/issues/39465) + /// mutable references from the same (immutable!) source. + /// This [error](https://github.com/rust-lang/rust/issues/39465) /// actually lead to an interim Rust release 1.15.1. /// /// **Known problems:** To be on the conservative side, if there's at least one - /// mutable reference - /// with the output lifetime, this lint will not trigger. In practice, this - /// case is unlikely anyway. + /// mutable reference with the output lifetime, this lint will not trigger. + /// In practice, this case is unlikely anyway. /// /// **Example:** /// ```ignore diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ea654467b86..e4361b00fb4 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -88,7 +88,7 @@ impl QuestionMark { replacement_str, applicability, ) - } + } } } } diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index d5797468e9d..fe457aad50e 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -16,8 +16,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore + /// // Bad /// let a = f(*&mut b); /// let c = *&d; + /// + /// // Good + /// let a = f(b); + /// let c = d; /// ``` pub DEREF_ADDROF, complexity, diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 30084e3e1ff..a2c35c42673 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -86,11 +86,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex { if let Some(span) = is_expn_of(expr.span, "regex"); then { if !self.spans.contains(&span) { - span_lint(cx, - REGEX_MACRO, - span, - "`regex!(_)` found. \ - Please use `Regex::new(_)`, which is faster for now."); + span_lint( + cx, + REGEX_MACRO, + span, + "`regex!(_)` found. \ + Please use `Regex::new(_)`, which is faster for now." + ); self.spans.insert(span); } self.last = Some(block.hir_id); diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 11360b0ef84..68c36f91891 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -25,7 +25,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let x = 1; + /// + /// // Bad /// let x = &x; + /// + /// // Good + /// let y = &x; // use different variable name /// ``` pub SHADOW_SAME, restriction, @@ -77,7 +82,12 @@ declare_clippy_lint! { /// # let y = 1; /// # let z = 2; /// let x = y; + /// + /// // Bad /// let x = z; // shadows the earlier binding + /// + /// // Good + /// let w = z; // use different variable name /// ``` pub SHADOW_UNRELATED, pedantic, diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 8d767a7fec8..2e853e8301d 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust, ignore + /// ```rust,ignore /// use regex; /// /// fn main() { @@ -24,7 +24,7 @@ declare_clippy_lint! { /// } /// ``` /// Better as - /// ```rust, ignore + /// ```rust,ignore /// fn main() { /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); /// } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index fb3706be1c2..a7c4f2c2291 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -22,11 +22,17 @@ declare_clippy_lint! { /// ```rust /// # use core::iter::repeat; /// # let len = 4; + /// + /// // Bad /// let mut vec1 = Vec::with_capacity(len); /// vec1.resize(len, 0); /// /// let mut vec2 = Vec::with_capacity(len); - /// vec2.extend(repeat(0).take(len)) + /// vec2.extend(repeat(0).take(len)); + /// + /// // Good + /// let mut vec1 = vec![0; len]; + /// let mut vec2 = vec![0; len]; /// ``` pub SLOW_VECTOR_INITIALIZATION, perf, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 2c51271e312..f84566ef707 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -24,6 +24,10 @@ declare_clippy_lint! { /// ```rust /// let mut x = "Hello".to_owned(); /// x = x + ", World"; + /// + /// // More readable + /// x += ", World"; + /// x.push_str(", World"); /// ``` pub STRING_ADD_ASSIGN, pedantic, @@ -69,7 +73,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// let bs = "a byte string".as_bytes(); + /// + /// // Good + /// let bs = b"a byte string"; /// ``` pub STRING_LIT_AS_BYTES, style, From 9893254dff38c2644612c8465ae9abfa553f4ea3 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 May 2020 12:08:41 +0200 Subject: [PATCH 0163/1110] Add more corrected code for doc --- clippy_lints/src/methods/mod.rs | 65 ++++++++++++++++++++------ clippy_lints/src/misc.rs | 2 +- clippy_lints/src/vec.rs | 10 +++- clippy_lints/src/verbose_file_reads.rs | 1 + clippy_lints/src/wildcard_imports.rs | 12 +++-- clippy_lints/src/write.rs | 22 ++++++++- clippy_lints/src/zero_div_zero.rs | 6 ++- src/lintlist/mod.rs | 2 +- 8 files changed, 95 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 52ca962e7ef..fbc29efdeb2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -218,7 +218,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let x = Ok::<_, ()>(()); - /// x.ok().expect("why did I do this again?") + /// + /// // Bad + /// x.ok().expect("why did I do this again?"); + /// + /// // Good + /// x.expect("why did I do this again?"); /// ``` pub OK_EXPECT, style, @@ -273,8 +278,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let opt = Some(1); - /// opt.map_or(None, |a| Some(a + 1)) - /// # ; + /// + /// // Bad + /// opt.map_or(None, |a| Some(a + 1)); + /// + /// // Good + /// opt.and_then(|a| Some(a + 1)); /// ``` pub OPTION_MAP_OR_NONE, style, @@ -390,14 +399,19 @@ declare_clippy_lint! { /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, /// /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// single method call using `_.flat_map(_)` /// /// **Known problems:** /// /// **Example:** /// ```rust /// let vec = vec![vec![1]]; + /// + /// // Bad /// vec.iter().map(|x| x.iter()).flatten(); + /// + /// // Good + /// vec.iter().flat_map(|x| x.iter()); /// ``` pub MAP_FLATTEN, pedantic, @@ -417,7 +431,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let vec = vec![1]; + /// + /// // Bad /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); + /// + /// // Good + /// vec.iter().filter_map(|x| Some(*x * 2)); /// ``` pub FILTER_MAP, pedantic, @@ -634,7 +653,12 @@ declare_clippy_lint! { /// ```rust /// # use std::rc::Rc; /// let x = Rc::new(1); + /// + /// // Bad /// x.clone(); + /// + /// // Good + /// Rc::clone(&x); /// ``` pub CLONE_ON_REF_PTR, restriction, @@ -741,7 +765,12 @@ declare_clippy_lint! { /// **Known problems:** Does not catch multi-byte unicode characters. /// /// **Example:** - /// `_.split("x")` could be `_.split('x')` + /// ```rust,ignore + /// // Bad + /// _.split("x"); + /// + /// // Good + /// _.split('x'); pub SINGLE_CHAR_PATTERN, perf, "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" @@ -964,8 +993,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `.chars().last()` or - /// `.chars().next_back()` on a `str` to check if it ends with a given char. + /// **What it does:** Checks for usage of `_.chars().last()` or + /// `_.chars().next_back()` on a `str` to check if it ends with a given char. /// /// **Why is this bad?** Readability, this can be written more concisely as /// `_.ends_with(_)`. @@ -975,8 +1004,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let name = "_"; - /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-') - /// # ; + /// + /// // Bad + /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); + /// + /// // Good + /// name.ends_with('_') || name.ends_with('-'); /// ``` pub CHARS_LAST_CMP, style, @@ -1044,17 +1077,15 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None }); - /// ``` - /// As there is no transformation of the argument this could be written as: - /// ```rust + /// + /// // As there is no transformation of the argument this could be written as: /// let _ = (0..3).filter(|&x| x > 2); /// ``` /// /// ```rust /// let _ = (0..4).filter_map(|x| Some(x + 1)); - /// ``` - /// As there is no conditional check on the argument this could be written as: - /// ```rust + /// + /// // As there is no conditional check on the argument this could be written as: /// let _ = (0..4).map(|x| x + 1); /// ``` pub UNNECESSARY_FILTER_MAP, @@ -1075,7 +1106,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let _ = (&vec![3, 4, 5]).into_iter(); + /// + /// // Good + /// let _ = (&vec![3, 4, 5]).iter(); /// ``` pub INTO_ITER_ON_REF, style, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index a3b7134a376..51282ab93ef 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { /// ```rust /// let x = 1.2331f64; /// let y = 1.2332f64; - /// + /// /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 1174f421577..a8d4c7620b1 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -17,8 +17,14 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust,ignore - /// foo(&vec![1, 2]) + /// ```rust + /// # fn foo(my_vec: &[u8]) {} + /// + /// // Bad + /// foo(&vec![1, 2]); + /// + /// // Good + /// foo(&[1, 2]); /// ``` pub USELESS_VEC, perf, diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs index 4d8d4438d88..7247518e19b 100644 --- a/clippy_lints/src/verbose_file_reads.rs +++ b/clippy_lints/src/verbose_file_reads.rs @@ -9,6 +9,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) + /// /// **Known problems:** None. /// /// **Example:** diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 32d9a45c37d..b637253bd02 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -19,8 +19,14 @@ declare_clippy_lint! { /// still around. /// /// **Example:** - /// ```rust + /// ```rust,ignore + /// // Bad /// use std::cmp::Ordering::*; + /// foo(Less); + /// + /// // Good + /// use std::cmp::Ordering; + /// foo(Ordering::Less) /// ``` pub ENUM_GLOB_USE, pedantic, @@ -60,15 +66,15 @@ declare_clippy_lint! { /// /// **Example:** /// - /// Bad: /// ```rust,ignore + /// // Bad /// use crate1::*; /// /// foo(); /// ``` /// - /// Good: /// ```rust,ignore + /// // Good /// use crate1::foo; /// /// foo(); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 5f794598052..22ce484b24e 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -23,7 +23,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// println!(""); + /// + /// // Good + /// println!(); /// ``` pub PRINTLN_EMPTY_STRING, style, @@ -32,8 +36,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** This lint warns when you use `print!()` with a format - /// string that - /// ends in a newline. + /// string that ends in a newline. /// /// **Why is this bad?** You should use `println!()` instead, which appends the /// newline. @@ -125,7 +128,12 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); + /// + /// // Bad /// writeln!(buf, ""); + /// + /// // Good + /// writeln!(buf); /// ``` pub WRITELN_EMPTY_STRING, style, @@ -147,7 +155,12 @@ declare_clippy_lint! { /// # use std::fmt::Write; /// # let mut buf = String::new(); /// # let name = "World"; + /// + /// // Bad /// write!(buf, "Hello {}!\n", name); + /// + /// // Good + /// writeln!(buf, "Hello {}!", name); /// ``` pub WRITE_WITH_NEWLINE, style, @@ -168,7 +181,12 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); + /// + /// // Bad /// writeln!(buf, "{}", "foo"); + /// + /// // Good + /// writeln!(buf, "foo"); /// ``` pub WRITE_LITERAL, style, diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index fb4700d8743..0820385e01b 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -14,7 +14,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// 0.0f32 / 0.0; + /// // Bad + /// let nan = 0.0f32 / 0.0; + /// + /// // Good + /// let nan = f32::NAN; /// ``` pub ZERO_DIVIDED_BY_ZERO, complexity, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ab9542a7b9c..6b6e2c7324c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1735,7 +1735,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "pub_enum_variant_names", group: "pedantic", - desc: "enums where all variants share a prefix/postfix", + desc: "public enums where all variants share a prefix/postfix", deprecation: None, module: "enum_variants", }, From 137a3b4d3242cfe331f8f9b87c51ac0c431fe160 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 1 Jun 2020 10:16:01 +0200 Subject: [PATCH 0164/1110] Corrected doc PR fixes --- clippy_lints/src/drop_bounds.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/eval_order_dependence.rs | 7 +++++-- clippy_lints/src/fallible_impl_from.rs | 12 ++++++++++-- clippy_lints/src/functions.rs | 2 +- clippy_lints/src/methods/mod.rs | 6 +++++- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index 4ef963ac314..5a7f759486e 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` /// Could be written as: /// ```rust - /// fn foo() {} + /// fn foo() {} /// ``` pub DROP_BOUNDS, correctness, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5f0cd1ec72c..d093025fd3d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// xs.map(|x| foo(x)) /// /// // Good - /// foo(xs) + /// xs.map(foo) /// ``` /// where `foo(_)` is a plain function that takes the exact argument type of /// `x`. diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 37e24ff34f7..74144fb299d 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -30,8 +30,11 @@ declare_clippy_lint! { /// // Unclear whether a is 1 or 2. /// /// // Good - /// x = 1; - /// let a = 1 + x; + /// let tmp = { + /// x = 1; + /// 1 + /// }; + /// let a = tmp + x; /// ``` pub EVAL_ORDER_DEPENDENCE, complexity, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 575462f25e6..92812816461 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust,ignore + /// ```rust /// struct Foo(i32); /// /// // Bad @@ -27,13 +27,21 @@ declare_clippy_lint! { /// Foo(s.parse().unwrap()) /// } /// } + /// ``` /// + /// ```rust /// // Good + /// struct Foo(i32); + /// /// use std::convert::TryFrom; /// impl TryFrom for Foo { /// type Error = (); /// fn try_from(s: String) -> Result { - /// s.parse() + /// if let Ok(parsed) = s.parse() { + /// Ok(Foo(parsed)) + /// } else { + /// Err(()) + /// } /// } /// } /// ``` diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 9b5f1dee7f4..325b6cf32a3 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -52,7 +52,7 @@ declare_clippy_lint! { /// ```rust /// fn im_too_long() { /// println!(""); - /// // ... 100 more LoC + /// // ... 100 more LoC /// println!(""); /// } /// ``` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fbc29efdeb2..a8d5c10d5da 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -436,7 +436,11 @@ declare_clippy_lint! { /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); /// /// // Good - /// vec.iter().filter_map(|x| Some(*x * 2)); + /// vec.iter().filter_map(|x| if *x == 0 { + /// Some(*x * 2) + /// } else { + /// None + /// }); /// ``` pub FILTER_MAP, pedantic, From 9e89ba93fda29b4dc707cd14bd518b73e676d895 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 May 2020 15:09:58 +0200 Subject: [PATCH 0165/1110] Add doc for checking if type defines certain method --- doc/common_tools_writing_lints.md | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index ed33b37c6bd..dbc43450594 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -4,7 +4,9 @@ You may need following tooltips to catch up with common operations. - [Common tools for writing lints](#common-tools-for-writing-lints) - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression) + - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait) + - [Checking if a type defines a method](#checking-if-a-type-defines-a-method) - [Dealing with macros](#dealing-with-macros) Useful Rustc dev guide links: @@ -49,6 +51,26 @@ Two noticeable items here: - `tables` is [`TypeckTables`][TypeckTables] and is created by type checking step, it includes useful information such as types of expressions, ways to resolve methods and so on. +# Checking if an expr is calling a specific method + +Starting with an `expr`, you can check whether it is calling a specific method `some_method`: + +```rust +impl LateLintPass<'_, '_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + // Check our expr is calling a method + if let hir::ExprKind::MethodCall(path, _, _args) = &expr.kind; + // Check the name of this method is `some_method` + if path.ident.name == sym!(some_method); + then { + // ... + } + } + } +} +``` + # Checking if a type implements a specific trait There are two ways to do this, depending if the target trait is part of lang items. @@ -83,6 +105,32 @@ A list of defined paths for Clippy can be found in [paths.rs][paths] We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. +# Checking if a type defines a specific method + +To check if our type defines a method called `some_method`: + +```rust +use crate::utils::{is_type_diagnostic_item, return_ty}; + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MyTypeImpl { + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) { + if_chain! { + // Check if item is a method/function + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + // Check the method is named `some_method` + if impl_item.ident.name == sym!(some_method); + // We can also check it has a parameter `self` + if signature.decl.implicit_self.has_implicit_self(); + // We can go further and even check if its return type is `String` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); + then { + // ... + } + } + } +} +``` + # Dealing with macros There are several helpers in Clippy's utils to deal with macros: From a44fa387efff44414c7baac249dcd148b93e2eb1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 1 Jun 2020 09:26:40 +0200 Subject: [PATCH 0166/1110] Update documentation on changelog updates --- CHANGELOG.md | 19 ++++++++++--------- doc/changelog_update.md | 9 +++++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc9895dd90..dd0905e9f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,17 +31,17 @@ Current beta, release 2020-07-16 * Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408) * Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622) * Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599) -* Generalize [`option_and_then_some`] and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529) -* Rename [`identity_conversion`] to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568) -* Merge [`block_in_if_condition_expr`] and [`block_in_if_condition_stmt`] into [`blocks_in_if_conditions`]. +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529) +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568) +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`]. [#5563](https://github.com/rust-lang/rust-clippy/pull/5563) -* Merge [`option_map_unwrap_or`], [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] into [`map_unwrap_or`]. +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`]. [#5563](https://github.com/rust-lang/rust-clippy/pull/5563) -* Merge [`option_unwrap_used`] and [`result_unwrap_used`] into [`unwrap_used`]. +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`]. [#5563](https://github.com/rust-lang/rust-clippy/pull/5563) -* Merge [`option_expect_used`] and [`result_expect_used`] into [`expect_used`]. +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`]. [#5563](https://github.com/rust-lang/rust-clippy/pull/5563) -* Merge [`for_loop_over_option`] and [`for_loop_over_result`] into [`for_loops_over_fallibles`]. +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`]. [#5563](https://github.com/rust-lang/rust-clippy/pull/5563) ### Enhancements @@ -61,10 +61,11 @@ Current beta, release 2020-07-16 * Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491) * Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602) * Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564) -* Add ignores to the list of words of [`clippy::doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611) +* Add ignores to the list of words of [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611) * Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636) * Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647) -* Honor lint level attributes for [`redundant_field_names`] and [`non_expressive_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651) +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`] +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651) * Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429) ### Suggestion Improvements diff --git a/doc/changelog_update.md b/doc/changelog_update.md index 0b80cce6d23..edba3b4741c 100644 --- a/doc/changelog_update.md +++ b/doc/changelog_update.md @@ -32,8 +32,9 @@ bullet points might be helpful: need to select the Rust release tag from the dropdown and then check the commit of the Clippy directory: - ![Explanation of how to find the commit hash](https://user-images.githubusercontent.com/2042399/62846160-1f8b0480-bcce-11e9-9da8-7964ca034e7a.png) - +To find the commit hash, click on "History" of the relevant branch in github +and search for the latest "Merge commit '' into " commit. +The part is then the most recent commit in the clippy repo. ### 2. Fetching the PRs between those commits @@ -74,5 +75,5 @@ relevant commit ranges. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ -[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools -[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools +[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy +[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy From fbf0b84b32aab798258838d5e932dbc56c4a1813 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 2 Jun 2020 21:42:33 +0700 Subject: [PATCH 0167/1110] Make use of slice pattern --- clippy_lints/src/utils/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 6c1488664bf..7e07e7751e3 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -165,8 +165,8 @@ pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&st /// Checks if an expression references a variable of the given name. pub fn match_var(expr: &Expr<'_>, var: Name) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == var { - return true; + if let [p] = path.segments { + return p.ident.name == var; } } false @@ -181,8 +181,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { match *path { - QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]), - QPath::Resolved(..) => None, + QPath::Resolved(_, ref path) => path.segments.get(0), QPath::TypeRelative(_, ref seg) => Some(seg), } } @@ -201,9 +200,12 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { QPath::Resolved(_, ref path) => match_path(path, segments), QPath::TypeRelative(ref ty, ref segment) => match ty.kind { TyKind::Path(ref inner_path) => { - !segments.is_empty() - && match_qpath(inner_path, &segments[..(segments.len() - 1)]) - && segment.ident.name.as_str() == segments[segments.len() - 1] + if let [prefix @ .., end] = segments { + if match_qpath(inner_path, prefix) { + return segment.ident.name.as_str() == *end; + } + } + false }, _ => false, }, From dcd480678236d3829f3af3ae9c3339e3e10aebad Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 2 Jun 2020 20:45:57 +0200 Subject: [PATCH 0168/1110] Apply suggestions from PR review --- CHANGELOG.md | 6 +++--- doc/changelog_update.md | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd0905e9f39..a8ec5fa67b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ Current beta, release 2020-07-16 ### Enhancements -* Avoid running cargo/internal lints when not enabled. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505) +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505) * Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631) * Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592) * Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616) @@ -61,7 +61,7 @@ Current beta, release 2020-07-16 * Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491) * Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602) * Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564) -* Add ignores to the list of words of [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611) +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611) * Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636) * Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647) * Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`] @@ -78,7 +78,7 @@ and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/565 ### ICE Fixes * Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590) -* Fix crash on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499) +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499) ### Documentation diff --git a/doc/changelog_update.md b/doc/changelog_update.md index edba3b4741c..1ec07b9dbbe 100644 --- a/doc/changelog_update.md +++ b/doc/changelog_update.md @@ -32,9 +32,10 @@ bullet points might be helpful: need to select the Rust release tag from the dropdown and then check the commit of the Clippy directory: -To find the commit hash, click on "History" of the relevant branch in github -and search for the latest "Merge commit '' into " commit. -The part is then the most recent commit in the clippy repo. +To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: +``` +git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" +``` ### 2. Fetching the PRs between those commits From b39fd5f62f80cb9bb47ac44d7100f694e0c7301c Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 3 Jun 2020 09:04:24 +0700 Subject: [PATCH 0169/1110] Fix false negative of checked_conversion lint --- clippy_lints/src/checked_conversions.rs | 78 ++++++++--------- tests/ui/checked_conversions.fixed | 106 +++++++++--------------- tests/ui/checked_conversions.rs | 106 +++++++++--------------- tests/ui/checked_conversions.stderr | 98 ++++++++++++++++------ tests/ui/checked_conversions.stdout | 0 5 files changed, 188 insertions(+), 200 deletions(-) delete mode 100644 tests/ui/checked_conversions.stdout diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d9776dd50a8..e845ef99c7c 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -58,24 +58,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions { } }; - if_chain! { - if let Some(cv) = result; - if let Some(to_type) = cv.to_type; - - then { + if let Some(cv) = result { + if let Some(to_type) = cv.to_type { let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut - applicability); + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); span_lint_and_sugg( cx, CHECKED_CONVERSIONS, item.span, "Checked cast can be simplified.", "try", - format!("{}::try_from({}).is_ok()", - to_type, - snippet), - applicability + format!("{}::try_from({}).is_ok()", to_type, snippet), + applicability, ); } } @@ -184,7 +178,7 @@ fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { if_chain! { if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind; if let Some((candidate, check)) = normalize_le_ge(op, left, right); - if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS); + if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); then { Conversion::try_new(candidate, from, to) @@ -224,7 +218,7 @@ fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> O /// Check for `expr >= (to_type::MIN as from_type)` fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { - if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) { + if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") { Conversion::try_new(candidate, from, to) } else { None @@ -232,10 +226,16 @@ fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Op } /// Tries to extract the from- and to-type from a cast expression -fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> { - // `to_type::maxmin_value() as from_type` +fn get_types_from_cast<'a>( + expr: &'a Expr<'_>, + types: &'a [&str], + func: &'a str, + assoc_const: &'a str, +) -> Option<(&'a str, &'a str)> { + // `to_type::max_value() as from_type` + // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { - // to_type::maxmin_value(), from_type + // to_type::max_value(), from_type if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; if let TyKind::Path(ref from_type_path) = &from_type.kind; if let Some(from_sym) = int_ty_to_sym(from_type_path); @@ -247,17 +247,17 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) } }; - // `from_type::from(to_type::maxmin_value())` + // `from_type::from(to_type::max_value())` let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { if_chain! { - // `from_type::from, to_type::maxmin_value()` + // `from_type::from, to_type::max_value()` if let ExprKind::Call(ref from_func, ref args) = &expr.kind; - // `to_type::maxmin_value()` + // `to_type::max_value()` if args.len() == 1; if let limit = &args[0]; // `from_type::from` if let ExprKind::Path(ref path) = &from_func.kind; - if let Some(from_sym) = get_implementing_type(path, INTS, FROM); + if let Some(from_sym) = get_implementing_type(path, INTS, "from"); then { Some((limit, from_sym)) @@ -268,22 +268,26 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) }); if let Some((limit, from_type)) = limit_from { - if_chain! { - if let ExprKind::Call(ref fun_name, _) = &limit.kind; - // `to_type, maxmin_value` - if let ExprKind::Path(ref path) = &fun_name.kind; - // `to_type` - if let Some(to_type) = get_implementing_type(path, types, func); - - then { - Some((from_type, to_type)) - } else { - None - } + match limit.kind { + // `from_type::from(_)` + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref path) = path.kind { + // `to_type` + if let Some(to_type) = get_implementing_type(path, types, func) { + return Some((from_type, to_type)); + } + } + }, + // `to_type::MAX` + ExprKind::Path(ref path) => { + if let Some(to_type) = get_implementing_type(path, types, assoc_const) { + return Some((from_type, to_type)); + } + }, + _ => {}, } - } else { - None - } + }; + None } /// Gets the type which implements the called function @@ -336,10 +340,6 @@ fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> O } // Constants -const FROM: &str = "from"; -const MAX_VALUE: &str = "max_value"; -const MIN_VALUE: &str = "min_value"; - const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"]; diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 7febd6f3761..12290db3dcf 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,106 +1,76 @@ // run-rustfix +#![allow( + clippy::cast_lossless, + // Int::max_value will be deprecated in the future + deprecated, +)] #![warn(clippy::checked_conversions)] -#![allow(clippy::cast_lossless)] -#![allow(dead_code)] + use std::convert::TryFrom; // Positive tests // Signed to unsigned -fn i64_to_u32(value: i64) -> Option { - if u32::try_from(value).is_ok() { - Some(value as u32) - } else { - None - } +pub fn i64_to_u32(value: i64) { + let _ = u32::try_from(value).is_ok(); + let _ = u32::try_from(value).is_ok(); } -fn i64_to_u16(value: i64) -> Option { - if u16::try_from(value).is_ok() { - Some(value as u16) - } else { - None - } +pub fn i64_to_u16(value: i64) { + let _ = u16::try_from(value).is_ok(); + let _ = u16::try_from(value).is_ok(); } -fn isize_to_u8(value: isize) -> Option { - if u8::try_from(value).is_ok() { - Some(value as u8) - } else { - None - } +pub fn isize_to_u8(value: isize) { + let _ = u8::try_from(value).is_ok(); + let _ = u8::try_from(value).is_ok(); } // Signed to signed -fn i64_to_i32(value: i64) -> Option { - if i32::try_from(value).is_ok() { - Some(value as i32) - } else { - None - } +pub fn i64_to_i32(value: i64) { + let _ = i32::try_from(value).is_ok(); + let _ = i32::try_from(value).is_ok(); } -fn i64_to_i16(value: i64) -> Option { - if i16::try_from(value).is_ok() { - Some(value as i16) - } else { - None - } +pub fn i64_to_i16(value: i64) { + let _ = i16::try_from(value).is_ok(); + let _ = i16::try_from(value).is_ok(); } // Unsigned to X -fn u32_to_i32(value: u32) -> Option { - if i32::try_from(value).is_ok() { - Some(value as i32) - } else { - None - } +pub fn u32_to_i32(value: u32) { + let _ = i32::try_from(value).is_ok(); + let _ = i32::try_from(value).is_ok(); } -fn usize_to_isize(value: usize) -> isize { - if isize::try_from(value).is_ok() && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn usize_to_isize(value: usize) { + let _ = isize::try_from(value).is_ok() && value as i32 == 5; + let _ = isize::try_from(value).is_ok() && value as i32 == 5; } -fn u32_to_u16(value: u32) -> isize { - if u16::try_from(value).is_ok() && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn u32_to_u16(value: u32) { + let _ = u16::try_from(value).is_ok() && value as i32 == 5; + let _ = u16::try_from(value).is_ok() && value as i32 == 5; } // Negative tests -fn no_i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= 0 { - Some(value as i32) - } else { - None - } +pub fn no_i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= 0; + let _ = value <= (i32::MAX as i64) && value >= 0; } -fn no_isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { - Some(value as u8) - } else { - None - } +pub fn no_isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize); + let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize); } -fn i8_to_u8(value: i8) -> Option { - if value >= 0 { - Some(value as u8) - } else { - None - } +pub fn i8_to_u8(value: i8) { + let _ = value >= 0; } fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index a643354e243..895a301e821 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,106 +1,76 @@ // run-rustfix +#![allow( + clippy::cast_lossless, + // Int::max_value will be deprecated in the future + deprecated, +)] #![warn(clippy::checked_conversions)] -#![allow(clippy::cast_lossless)] -#![allow(dead_code)] + use std::convert::TryFrom; // Positive tests // Signed to unsigned -fn i64_to_u32(value: i64) -> Option { - if value <= (u32::max_value() as i64) && value >= 0 { - Some(value as u32) - } else { - None - } +pub fn i64_to_u32(value: i64) { + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; } -fn i64_to_u16(value: i64) -> Option { - if value <= i64::from(u16::max_value()) && value >= 0 { - Some(value as u16) - } else { - None - } +pub fn i64_to_u16(value: i64) { + let _ = value <= i64::from(u16::max_value()) && value >= 0; + let _ = value <= i64::from(u16::MAX) && value >= 0; } -fn isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= 0 { - Some(value as u8) - } else { - None - } +pub fn isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= 0; + let _ = value <= (u8::MAX as isize) && value >= 0; } // Signed to signed -fn i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { - Some(value as i32) - } else { - None - } +pub fn i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); + let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); } -fn i64_to_i16(value: i64) -> Option { - if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { - Some(value as i16) - } else { - None - } +pub fn i64_to_i16(value: i64) { + let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); + let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); } // Unsigned to X -fn u32_to_i32(value: u32) -> Option { - if value <= i32::max_value() as u32 { - Some(value as i32) - } else { - None - } +pub fn u32_to_i32(value: u32) { + let _ = value <= i32::max_value() as u32; + let _ = value <= i32::MAX as u32; } -fn usize_to_isize(value: usize) -> isize { - if value <= isize::max_value() as usize && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn usize_to_isize(value: usize) { + let _ = value <= isize::max_value() as usize && value as i32 == 5; + let _ = value <= isize::MAX as usize && value as i32 == 5; } -fn u32_to_u16(value: u32) -> isize { - if value <= u16::max_value() as u32 && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn u32_to_u16(value: u32) { + let _ = value <= u16::max_value() as u32 && value as i32 == 5; + let _ = value <= u16::MAX as u32 && value as i32 == 5; } // Negative tests -fn no_i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= 0 { - Some(value as i32) - } else { - None - } +pub fn no_i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= 0; + let _ = value <= (i32::MAX as i64) && value >= 0; } -fn no_isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { - Some(value as u8) - } else { - None - } +pub fn no_isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize); + let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize); } -fn i8_to_u8(value: i8) -> Option { - if value >= 0 { - Some(value as u8) - } else { - None - } +pub fn i8_to_u8(value: i8) { + let _ = value >= 0; } fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index f678f009621..648ba3ccd01 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,52 +1,100 @@ error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:13:8 + --> $DIR/checked_conversions.rs:17:13 | -LL | if value <= (u32::max_value() as i64) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` +LL | let _ = value <= (u32::max_value() as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` | = note: `-D clippy::checked-conversions` implied by `-D warnings` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:21:8 + --> $DIR/checked_conversions.rs:18:13 | -LL | if value <= i64::from(u16::max_value()) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:29:8 + --> $DIR/checked_conversions.rs:22:13 | -LL | if value <= (u8::max_value() as isize) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` +LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:39:8 + --> $DIR/checked_conversions.rs:23:13 | -LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` +LL | let _ = value <= i64::from(u16::MAX) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:47:8 + --> $DIR/checked_conversions.rs:27:13 | -LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` +LL | let _ = value <= (u8::max_value() as isize) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:57:8 + --> $DIR/checked_conversions.rs:28:13 | -LL | if value <= i32::max_value() as u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` +LL | let _ = value <= (u8::MAX as isize) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:65:8 + --> $DIR/checked_conversions.rs:34:13 | -LL | if value <= isize::max_value() as usize && value as i32 == 5 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` +LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:73:8 + --> $DIR/checked_conversions.rs:35:13 | -LL | if value <= u16::max_value() as u32 && value as i32 == 5 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` +LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: aborting due to 8 previous errors +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:39:13 + | +LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:40:13 + | +LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:46:13 + | +LL | let _ = value <= i32::max_value() as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:47:13 + | +LL | let _ = value <= i32::MAX as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:51:13 + | +LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:52:13 + | +LL | let _ = value <= isize::MAX as usize && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:56:13 + | +LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:57:13 + | +LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: aborting due to 16 previous errors diff --git a/tests/ui/checked_conversions.stdout b/tests/ui/checked_conversions.stdout deleted file mode 100644 index e69de29bb2d..00000000000 From 7654125d27d95d5c329e554115b510efc1ab1e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 4 Jun 2020 03:34:22 +0200 Subject: [PATCH 0170/1110] match_wildcard_for_single_variants: remove empty line at start of lint example. See https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants changelog: none --- clippy_lints/src/matches.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 146212cb2c7..6d7af45a472 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -270,7 +270,6 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A, B, C } /// # let x = Foo::B; - /// /// // Bad /// match x { /// Foo::A => {}, From 9ef15ae7c86a08c96a368f6728b25e1c55f6e10b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 5 Jun 2020 13:56:07 +0200 Subject: [PATCH 0171/1110] Reorder sections of release documentation Before tagging a commit the beta branch has to be remerged --- doc/release.md | 120 ++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/doc/release.md b/doc/release.md index 9d69fa8a7f6..f0a7fe52149 100644 --- a/doc/release.md +++ b/doc/release.md @@ -7,11 +7,11 @@ Clippy is released together with stable Rust releases. The dates for these releases can be found at the [Rust Forge]. This document explains the necessary steps to create a Clippy release. -1. [Find the Clippy commit](#find-the-clippy-commit) -2. [Tag the stable commit](#tag-the-stable-commit) -3. [Update `CHANGELOG.md`](#update-changelogmd) -4. [Remerge the `beta` branch](#remerge-the-beta-branch) -5. [Update the `beta` branch](#update-the-beta-branch) +1. [Remerge the `beta` branch](#remerge-the-beta-branch) +2. [Update the `beta` branch](#update-the-beta-branch) +3. [Find the Clippy commit](#find-the-clippy-commit) +4. [Tag the stable commit](#tag-the-stable-commit) +5. [Update `CHANGELOG.md`](#update-changelogmd) _NOTE: This document is for stable Rust releases, not for point releases. For point releases, step 1. and 2. should be enough._ @@ -19,6 +19,61 @@ point releases, step 1. and 2. should be enough._ [Rust Forge]: https://forge.rust-lang.org/ +## Remerge the `beta` branch + +This step is only necessary, if since the last release something was backported +to the beta Rust release. The remerge is then necessary, to make sure that the +Clippy commit, that was used by the now stable Rust release, persists in the +tree of the Clippy repository. + +To find out if this step is necessary run + +```bash +# Assumes that the local master branch is up-to-date +$ git fetch upstream +$ git branch master --contains upstream/beta +``` + +If this command outputs `master`, this step is **not** necessary. + +```bash +# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy +$ git checkout -b backport_remerge +$ git merge upstream/beta +$ git diff # This diff has to be empty, otherwise something with the remerge failed +$ git push origin backport_remerge # This can be pushed to your fork +``` + +After this, open a PR to the master branch. In this PR, the commit hash of the +`HEAD` of the `beta` branch must exists. In addition to that, no files should +be changed by this PR. + + +## Update the `beta` branch + +This step must be done **after** the PR of the previous step was merged. + +First, the Clippy commit of the `beta` branch of the Rust repository has to be +determined. + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git checkout beta +$ git submodule update +$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}') +``` + +After finding the Clippy commit, the `beta` branch in the Clippy repository can +be updated. + +```bash +# Assuming the current directory corresponds to the Clippy repository +$ git checkout beta +$ git rebase $BETA_SHA +$ git push upstream beta +``` + + ## Find the Clippy commit The first step is to tag the Clippy commit, that is included in the stable Rust @@ -54,58 +109,3 @@ After this, the release should be available on the Clippy [release page]. For this see the document on [how to update the changelog]. [how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md - - -## Remerge the `beta` branch - -This step is only necessary, if since the last release something was backported -to the beta Rust release. The remerge is then necessary, to make sure that the -Clippy commit, that was used by the now stable Rust release, persists in the -tree of the Clippy repository. - -To find out if this step is necessary run - -```bash -# Assumes that the local master branch is up-to-date -$ git fetch upstream -$ git branch master --contains upstream/beta -``` - -If this command outputs `master`, this step is **not** necessary. - -```bash -# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy -$ git checkout -b backport_remerge -$ git merge beta -$ git diff # This diff has to be empty, otherwise something with the remerge failed -$ git push origin backport_remerge # This can be pushed to your fork -``` - -After this, open a PR to the master branch. In this PR, the commit hash of the -`HEAD` of the `beta` branch must exists. In addition to that, no files should -be changed by this PR. - - -## Update the `beta` branch - -This step must be done **after** the PR of the previous step was merged. - -First, the Clippy commit of the `beta` branch of the Rust repository has to be -determined. - -```bash -# Assuming the current directory corresponds to the Rust repository -$ git checkout beta -$ git submodule update -$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}') -``` - -After finding the Clippy commit, the `beta` branch in the Clippy repository can -be updated. - -```bash -# Assuming the current directory corresponds to the Clippy repository -$ git checkout beta -$ git rebase $BETA_SHA -$ git push upstream beta -``` From 6b9e2e90bf7688bfbcf357fda6e0ef74e11ba1ff Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 5 Jun 2020 14:47:12 +0200 Subject: [PATCH 0172/1110] Replace all remaining occurrences of submodule with subtree --- doc/changelog_update.md | 2 +- doc/release.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/changelog_update.md b/doc/changelog_update.md index 0b80cce6d23..6657ab4187b 100644 --- a/doc/changelog_update.md +++ b/doc/changelog_update.md @@ -18,7 +18,7 @@ been very rare that Clippy changes were included in a patch release. ### 1. Finding the relevant Clippy commits -Each Rust release ships with its own version of Clippy. The Clippy submodule can +Each Rust release ships with its own version of Clippy. The Clippy subtree can be found in the `tools` directory of the Rust repository. Depending on the current time and what exactly you want to update, the following diff --git a/doc/release.md b/doc/release.md index f0a7fe52149..391952ea6b1 100644 --- a/doc/release.md +++ b/doc/release.md @@ -59,8 +59,7 @@ determined. ```bash # Assuming the current directory corresponds to the Rust repository $ git checkout beta -$ git submodule update -$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}') +$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` After finding the Clippy commit, the `beta` branch in the Clippy repository can @@ -83,8 +82,7 @@ release. This commit can be found in the Rust repository. # Assuming the current directory corresponds to the Rust repository $ git fetch upstream # `upstream` is the `rust-lang/rust` remote $ git checkout 1.XX.0 # XX should be exchanged with the corresponding version -$ git submodule update -$ SHA=$(git submodule status src/tools/clippy | awk '{print $1}') +$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` From 413713c8848ec94d5f1709a41537c300da398806 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 5 Jun 2020 22:28:58 +0200 Subject: [PATCH 0173/1110] Add error info when cargo metadata fails to run --- clippy_lints/src/cargo_common_metadata.rs | 13 ++----------- clippy_lints/src/multiple_crate_versions.rs | 10 ++-------- clippy_lints/src/utils/mod.rs | 18 ++++++++++++++++++ clippy_lints/src/wildcard_dependencies.rs | 7 +------ 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 16b46423c8f..c40a387d297 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -36,13 +36,9 @@ declare_clippy_lint! { "common metadata is defined in `Cargo.toml`" } -fn warning(cx: &LateContext<'_, '_>, message: &str) { - span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message); -} - fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) { let message = format!("package `{}` is missing `{}` metadata", package.name, field); - warning(cx, &message); + span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); } fn is_empty_str(value: &Option) -> bool { @@ -66,12 +62,7 @@ impl LateLintPass<'_, '_> for CargoCommonMetadata { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() { - metadata - } else { - warning(cx, "could not read cargo metadata"); - return; - }; + let metadata = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false); for package in metadata.packages { if is_empty_vec(&package.authors) { diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index b6770804e18..6c42014b4c8 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; -use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId}; +use cargo_metadata::{DependencyKind, Node, Package, PackageId}; use if_chain::if_chain; use itertools::Itertools; @@ -42,13 +42,7 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { return; } - let metadata = if let Ok(metadata) = MetadataCommand::new().exec() { - metadata - } else { - span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata"); - return; - }; - + let metadata = unwrap_cargo_metadata!(cx, MULTIPLE_CRATE_VERSIONS, true); let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str(); let mut packages = metadata.packages; packages.sort_by(|a, b| a.name.cmp(&b.name)); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 7e07e7751e3..2cdb6486fcb 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1405,6 +1405,24 @@ pub fn run_lints(cx: &LateContext<'_, '_>, lints: &[&'static Lint], id: HirId) - }) } +#[macro_export] +macro_rules! unwrap_cargo_metadata { + ($cx: ident, $lint: ident, $deps: expr) => {{ + let mut command = cargo_metadata::MetadataCommand::new(); + if !$deps { + command.no_deps(); + } + + match command.exec() { + Ok(metadata) => metadata, + Err(err) => { + span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err)); + return; + }, + } + }}; +} + #[cfg(test)] mod test { use super::{trim_multiline, without_block_comments}; diff --git a/clippy_lints/src/wildcard_dependencies.rs b/clippy_lints/src/wildcard_dependencies.rs index d8d48eb1535..511518082be 100644 --- a/clippy_lints/src/wildcard_dependencies.rs +++ b/clippy_lints/src/wildcard_dependencies.rs @@ -34,12 +34,7 @@ impl LateLintPass<'_, '_> for WildcardDependencies { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() { - metadata - } else { - span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata"); - return; - }; + let metadata = unwrap_cargo_metadata!(cx, WILDCARD_DEPENDENCIES, false); for dep in &metadata.packages[0].dependencies { // VersionReq::any() does not work From c325c120c21657acb1b131ded41261889e51a62b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 5 Jun 2020 22:30:14 +0200 Subject: [PATCH 0174/1110] Fix cargo ui tests when running inside rust repo --- clippy_dev/src/new_lint.rs | 2 ++ tests/compile-test.rs | 4 ---- tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml | 2 ++ tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 2 ++ .../multiple_crate_versions/5041_allow_dev_build/Cargo.toml | 2 ++ tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml | 2 ++ tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml | 2 ++ tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml | 2 ++ tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml | 2 ++ 9 files changed, 16 insertions(+), 4 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index c0b2dac2f60..1e032a7bc20 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -147,6 +147,8 @@ fn get_manifest_contents(lint_name: &str, hint: &str) -> String { name = "{}" version = "0.1.0" publish = false + +[workspace] "#, hint, lint_name ) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 194354b291f..11b3f69a828 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -220,10 +220,6 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } - if cargo::is_rustc_test_suite() { - return; - } - config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml index c64adcf7c01..ae0a6032996 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -2,3 +2,5 @@ name = "cargo_common_metadata" version = "0.1.0" publish = false + +[workspace] diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index c8233f328bb..737e84e963c 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -9,3 +9,5 @@ readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["metadata", "lint", "clippy"] categories = ["development-tools::testing"] + +[workspace] diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml index 72731fbc75d..278bebbbd9e 100644 --- a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml @@ -5,6 +5,8 @@ name = "multiple_crate_versions" version = "0.1.0" publish = false +[workspace] + # One of the versions of winapi is only a dev dependency: allowed [dependencies] ctrlc = "=3.1.0" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml index 3a94b723f3f..4f97b011334 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -3,6 +3,8 @@ name = "multiple_crate_versions" version = "0.1.0" publish = false +[workspace] + [dependencies] ctrlc = "=3.1.0" ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml index a9b06420b33..b4b49bb369a 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -3,6 +3,8 @@ name = "cargo_common_metadata" version = "0.1.0" publish = false +[workspace] + [dependencies] regex = "1.3.7" serde = "1.0.110" diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml index fd2a3414856..3e1a02cbb3c 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml @@ -3,5 +3,7 @@ name = "wildcard_dependencies" version = "0.1.0" publish = false +[workspace] + [dependencies] regex = "*" diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml index 38cb139146e..f844cab09ba 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml @@ -3,5 +3,7 @@ name = "wildcard_dependencies" version = "0.1.0" publish = false +[workspace] + [dependencies] regex = "1" From 623faac84ec56fa545163ab81d7e3b759a392353 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 6 Jun 2020 11:50:59 +0200 Subject: [PATCH 0175/1110] Cleanup: Use rustc's `same_types` instead of our `same_tys` --- clippy_lints/src/copies.rs | 14 +++++--------- clippy_lints/src/loops.rs | 8 ++++---- clippy_lints/src/methods/mod.rs | 12 ++++++------ clippy_lints/src/new_without_default.rs | 6 +++--- clippy_lints/src/types.rs | 6 +++--- clippy_lints/src/useless_conversion.rs | 14 +++++++------- clippy_lints/src/utils/mod.rs | 16 +--------------- 7 files changed, 29 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 66722786eab..b6d50bdfa14 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,9 +1,9 @@ -use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span_lint_and_note, span_lint_and_then}; +use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; use crate::utils::{SpanlessEq, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; use std::collections::hash_map::Entry; @@ -242,15 +242,11 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) { /// Implementation of `MATCH_SAME_ARMS`. fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) { - fn same_bindings<'tcx>( - cx: &LateContext<'_, 'tcx>, - lhs: &FxHashMap>, - rhs: &FxHashMap>, - ) -> bool { + fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { lhs.len() == rhs.len() && lhs .iter() - .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty))) + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) } if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { @@ -269,7 +265,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) { (min_index..=max_index).all(|index| arms[index].guard.is_none()) && SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && // all patterns should have the same bindings - same_bindings(cx, &bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) + same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) }; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index dbe41823a9c..57c62d73964 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -8,7 +8,7 @@ use crate::utils::{ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg}; +use crate::utils::{is_type_diagnostic_item, qpath_res, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -24,7 +24,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::region; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -1256,7 +1256,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { let receiver_ty = cx.tables.expr_ty(&args[0]); let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]); - if same_tys(cx, receiver_ty, receiver_ty_adjusted) { + if TyS::same_type(receiver_ty, receiver_ty_adjusted) { let mut applicability = Applicability::MachineApplicable; let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); span_lint_and_sugg( @@ -1277,7 +1277,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e mutbl: Mutability::Not, }, ); - if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) { + if TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) { lint_iter_method(cx, args, arg, method_name) } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a9c0ff24fa6..78d69690c2d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -18,7 +18,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -29,9 +29,9 @@ use crate::utils::{ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, + remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, + walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1548,7 +1548,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { let contains_self_ty = |ty: Ty<'tcx>| { ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => same_tys(cx, self_ty, inner_ty), + GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }) @@ -1575,7 +1575,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { } } - if name == "new" && !same_tys(cx, ret_ty, self_ty) { + if name == "new" && !TyS::same_type(ret_ty, self_ty) { span_lint( cx, NEW_RET_NO_SELF, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index e556e5d59c1..dd236535c18 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,13 +1,13 @@ use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then}; +use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirIdSet; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -93,7 +93,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); let self_ty = cx.tcx.type_of(self_def_id); if_chain! { - if same_tys(cx, self_ty, return_ty(cx, id)); + if TyS::same_type(self_ty, return_ty(cx, id)); if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); then { if self.impling_types.is_none() { diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5ca30d598eb..bc5fe44b30f 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -17,7 +17,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TypeckTables}; +use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckTables}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; @@ -31,7 +31,7 @@ use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, same_tys, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, + qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; @@ -2556,7 +2556,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; then { - if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) { + if !TyS::same_type(self.target.ty(), self.body.expr_ty(e)) { return; } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 7fa97b24699..141035a980a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,12 +1,12 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, + is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -65,7 +65,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.tables.expr_ty(e); let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { + if TyS::same_type(a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); span_lint_and_sugg( cx, @@ -81,7 +81,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { let a = cx.tables.expr_ty(e); let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { + if TyS::same_type(a, b) { let sugg = snippet(cx, args[0].span, "").into_owned(); span_lint_and_sugg( cx, @@ -101,7 +101,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; if let Some(a_type) = substs.types().next(); - if same_tys(cx, a_type, b); + if TyS::same_type(a_type, b); then { span_lint_and_help( @@ -131,7 +131,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; if let Some(a_type) = substs.types().next(); - if same_tys(cx, a_type, b); + if TyS::same_type(a_type, b); then { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); @@ -148,7 +148,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::FROM_FROM); - if same_tys(cx, a, b); + if TyS::same_type(a, b); then { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2cdb6486fcb..9a6750c51ab 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -40,7 +40,7 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -879,20 +879,6 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> T cx.tcx.erase_late_bound_regions(&ret_ty) } -/// Checks if two types are the same. -/// -/// This discards any lifetime annotations, too. -// -// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` == -// `for <'b> Foo<'b>`, but not for type parameters). -pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a)); - let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b)); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok()) -} - /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind { From 5bdbc45ae579e7b8f4187bc791abd67924cb626b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 7 Jun 2020 03:07:48 +0200 Subject: [PATCH 0176/1110] Rustup to rust-lang/rust#71796 --- tests/ui/or_fun_call.fixed | 4 ++-- tests/ui/or_fun_call.stderr | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 7bb08797ef3..2045ffdb5f0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -29,7 +29,7 @@ fn or_fun_call() { with_enum.unwrap_or(Enum::A(5)); let with_const_fn = Some(Duration::from_secs(1)); - with_const_fn.unwrap_or(Duration::from_secs(5)); + with_const_fn.unwrap_or_else(|| Duration::from_secs(5)); let with_constructor = Some(vec![1]); with_constructor.unwrap_or_else(make); @@ -94,7 +94,7 @@ fn test_or_with_ctors() { let b = "b".to_string(); let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) - .or(Some(Bar(b, Duration::from_secs(2)))); + .or_else(|| Some(Bar(b, Duration::from_secs(2)))); let vec = vec!["foo"]; let _ = opt.ok_or(vec.len()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 96d55771e6c..bc5978b538f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,10 +1,16 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:32:19 + | +LL | with_const_fn.unwrap_or(Duration::from_secs(5)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + error: use of `unwrap_or` followed by a function call --> $DIR/or_fun_call.rs:35:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` - | - = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a call to `new` --> $DIR/or_fun_call.rs:38:5 @@ -78,5 +84,11 @@ error: use of `or` followed by a function call LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` -error: aborting due to 13 previous errors +error: use of `or` followed by a function call + --> $DIR/or_fun_call.rs:97:10 + | +LL | .or(Some(Bar(b, Duration::from_secs(2)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` + +error: aborting due to 15 previous errors From d9aa26a14dca32e0c4f2718ad1d3322f0de1674d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 7 Jun 2020 14:54:21 +0200 Subject: [PATCH 0177/1110] Temporarily disable RLS integration test --- .github/workflows/clippy_bors.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 3958ba01246..0c80394f03e 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -232,7 +232,8 @@ jobs: matrix: integration: - 'rust-lang/cargo' - - 'rust-lang/rls' + # FIXME: re-enable once fmt_macros is renamed in RLS + # - 'rust-lang/rls' - 'rust-lang/chalk' - 'rust-lang/rustfmt' - 'Marwes/combine' From dc13016ac2df1ce2663660389409b15eb2cf7e40 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 3 Jun 2020 01:13:57 +0200 Subject: [PATCH 0178/1110] Make let_and_return a late lint pass --- clippy_lints/src/let_and_return.rs | 82 ++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 8 +-- clippy_lints/src/returns.rs | 82 ++---------------------------- src/lintlist/mod.rs | 2 +- 4 files changed, 91 insertions(+), 83 deletions(-) create mode 100644 clippy_lints/src/let_and_return.rs diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs new file mode 100644 index 00000000000..8b877f696af --- /dev/null +++ b/clippy_lints/src/let_and_return.rs @@ -0,0 +1,82 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Block, ExprKind, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_lint_pass!(LetReturn => [LET_AND_RETURN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if_chain! { + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + then { + span_lint_and_then( + cx, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(snippet) = snippet_opt(cx, initexpr.span) { + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f8923b2660..6bc9e23bac5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -239,6 +239,7 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; +mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -596,6 +597,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, + &let_and_return::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -772,7 +774,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::REGEX_MACRO, ®ex::TRIVIAL_REGEX, - &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, @@ -1022,6 +1023,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box formatting::Formatting); store.register_early_pass(|| box misc_early::MiscEarlyLints); store.register_early_pass(|| box returns::Return); + store.register_late_pass(|| box let_and_return::LetReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1265,6 +1267,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1390,7 +1393,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::REGEX_MACRO), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), @@ -1474,6 +1476,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1526,7 +1529,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::REGEX_MACRO), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 35464f629c3..3c939744173 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for return statements at the end of a block. @@ -36,33 +36,6 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. /// @@ -90,7 +63,7 @@ enum RetReplacement { Block, } -declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]); +declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]); impl Return { // Check the final stmt or expr in a block for unnecessary return. @@ -105,7 +78,7 @@ impl Return { } } - // Check a the final expression in a block if it's a return. + // Check the final expression in a block if it's a return. fn check_final_expr( &mut self, cx: &EarlyContext<'_>, @@ -186,54 +159,6 @@ impl Return { }, } } - - // Check for "let x = EXPR; x" - fn check_let_return(cx: &EarlyContext<'_>, block: &ast::Block) { - let mut it = block.stmts.iter(); - - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = it.next_back(); - if let ast::StmtKind::Expr(ref retexpr) = retexpr.kind; - if let Some(stmt) = it.next_back(); - if let ast::StmtKind::Local(ref local) = stmt.kind; - // don't lint in the presence of type inference - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(ref initexpr) = local.init; - if let ast::PatKind::Ident(_, ident, _) = local.pat.kind; - if let ast::ExprKind::Path(_, ref path) = retexpr.kind; - if match_path_ast(path, &[&*ident.name.as_str()]); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } } impl EarlyLintPass for Return { @@ -254,7 +179,6 @@ impl EarlyLintPass for Return { } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - Self::check_let_return(cx, block); if_chain! { if let Some(ref stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d5d07ccb2eb..bb191f9be92 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1023,7 +1023,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", deprecation: None, - module: "returns", + module: "let_and_return", }, Lint { name: "let_underscore_lock", From 9c205d7b1baa982ae7063d57b18088ecf28df83b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 6 Jun 2020 21:51:41 +0200 Subject: [PATCH 0179/1110] Rename let_and_return test for consistency with the lint name --- tests/ui/{let_return.rs => let_and_return.rs} | 0 tests/ui/{let_return.stderr => let_and_return.stderr} | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/ui/{let_return.rs => let_and_return.rs} (100%) rename tests/ui/{let_return.stderr => let_and_return.stderr} (89%) diff --git a/tests/ui/let_return.rs b/tests/ui/let_and_return.rs similarity index 100% rename from tests/ui/let_return.rs rename to tests/ui/let_and_return.rs diff --git a/tests/ui/let_return.stderr b/tests/ui/let_and_return.stderr similarity index 89% rename from tests/ui/let_return.stderr rename to tests/ui/let_and_return.stderr index 128a22c86e3..eacf948b392 100644 --- a/tests/ui/let_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -1,5 +1,5 @@ error: returning the result of a `let` binding from a block - --> $DIR/let_return.rs:7:5 + --> $DIR/let_and_return.rs:7:5 | LL | let x = 5; | ---------- unnecessary `let` binding @@ -14,7 +14,7 @@ LL | 5 | error: returning the result of a `let` binding from a block - --> $DIR/let_return.rs:13:9 + --> $DIR/let_and_return.rs:13:9 | LL | let x = 5; | ---------- unnecessary `let` binding From dac8a3c1ca19d2b5934ecbe2ed79ae6c156fd885 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 7 Jun 2020 00:30:39 +0200 Subject: [PATCH 0180/1110] let_and_return: do not lint if last statement borrows --- clippy_lints/src/let_and_return.rs | 61 ++++++++++++++++++++++++++- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/let_and_return.rs | 68 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs index 8b877f696af..6d3fb317bcf 100644 --- a/clippy_lints/src/let_and_return.rs +++ b/clippy_lints/src/let_and_return.rs @@ -1,8 +1,12 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Block, ExprKind, PatKind, StmtKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; @@ -49,6 +53,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { if let PatKind::Binding(.., ident, _) = local.pat.kind; if let ExprKind::Path(qpath) = &retexpr.kind; if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); if !in_external_macro(cx.sess(), initexpr.span); if !in_external_macro(cx.sess(), retexpr.span); if !in_external_macro(cx.sess(), local.span); @@ -80,3 +85,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { } } } + +fn last_statement_borrows<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + borrows: bool, +} + +impl BorrowVisitor<'_, '_> { + fn fn_def_id(&self, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => self.cx.tables.type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => self.cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } + } +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = self.fn_def_id(expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 06638e7187b..39410acea4e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -400,7 +400,7 @@ pub fn method_calls<'tcx>( /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s. /// /// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`, -/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec` +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec` /// containing the `Expr`s for /// `.bar()` and `.baz()` pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option]>> { diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 23645d48fe7..09614b8c1ad 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -67,4 +67,72 @@ macro_rules! tuple_encode { tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); +mod no_lint_if_stmt_borrows { + mod issue_3792 { + use std::io::{self, BufRead, Stdin}; + + fn read_line() -> String { + let stdin = io::stdin(); + let line = stdin.lock().lines().next().unwrap().unwrap(); + line + } + } + + mod issue_3324 { + use std::cell::RefCell; + use std::rc::{Rc, Weak}; + + fn test(value: Weak>) -> u32 { + let value = value.upgrade().unwrap(); + let ret = value.borrow().baz(); + ret + } + + struct Bar {} + + impl Bar { + fn new() -> Self { + Bar {} + } + fn baz(&self) -> u32 { + 0 + } + } + + fn main() { + let a = Rc::new(RefCell::new(Bar::new())); + let b = Rc::downgrade(&a); + test(b); + } + } + + mod free_function { + struct Inner; + + struct Foo<'a> { + inner: &'a Inner, + } + + impl Drop for Foo<'_> { + fn drop(&mut self) {} + } + + impl Foo<'_> { + fn value(&self) -> i32 { + 42 + } + } + + fn some_foo(inner: &Inner) -> Foo<'_> { + Foo { inner } + } + + fn test() -> i32 { + let x = Inner {}; + let value = some_foo(&x).value(); + value + } + } +} + fn main() {} From ebfc1da07d2cd1cba87a3df79c5ffbfc0d25618c Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 7 Jun 2020 20:38:28 +0200 Subject: [PATCH 0181/1110] reversed_empty_ranges: don't lint N..N except in for loop arg --- clippy_lints/src/ranges.rs | 62 +++++++++---------- tests/ui/reversed_empty_ranges_fixable.fixed | 8 +-- tests/ui/reversed_empty_ranges_fixable.rs | 8 +-- tests/ui/reversed_empty_ranges_fixable.stderr | 16 ++--- tests/ui/reversed_empty_ranges_unfixable.rs | 5 +- .../ui/reversed_empty_ranges_unfixable.stderr | 20 +++--- 6 files changed, 52 insertions(+), 67 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 1eb26d97ed4..45de4d29375 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -241,14 +241,26 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> { - match get_parent_expr(cx, expr) { - parent_expr @ Some(Expr { + fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { kind: ExprKind::Index(..), .. - }) => parent_expr, - _ => None, + }) + ) + } + + fn is_for_loop_arg(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let mut cur_expr = expr; + while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { + match higher::for_loop(parent_expr) { + Some((_, args, _)) if args.hir_id == expr.hir_id => return true, + _ => cur_expr = parent_expr, + } } + + false } fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { @@ -267,34 +279,18 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { - if let Some(parent_expr) = inside_indexing_expr(cx, expr) { - let (reason, outcome) = if ordering == Ordering::Equal { - ("empty", "always yield an empty slice") - } else { - ("reversed", "panic at run-time") - }; - - span_lint_and_then( - cx, - REVERSED_EMPTY_RANGES, - expr.span, - &format!("this range is {} and using it to index a slice will {}", reason, outcome), - |diag| { - if_chain! { - if ordering == Ordering::Equal; - if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind; - then { - diag.span_suggestion( - parent_expr.span, - "if you want an empty slice, use", - format!("[] as &[{}]", slice_ty), - Applicability::MaybeIncorrect - ); - } - } - } - ); - } else { + if inside_indexing_expr(cx, expr) { + // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... + if ordering != Ordering::Equal { + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is reversed and using it to index a slice will panic at run-time", + ); + } + // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` + } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { span_lint_and_then( cx, REVERSED_EMPTY_RANGES, diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index 332c0427ef6..79e482eec30 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -4,8 +4,6 @@ const ANSWER: i32 = 42; fn main() { - let arr = [1, 2, 3, 4, 5]; - // These should be linted: (21..=42).rev().for_each(|x| println!("{}", x)); @@ -14,16 +12,18 @@ fn main() { for _ in (-42..=-21).rev() {} for _ in (21u32..42u32).rev() {} - let _ = &[] as &[i32]; - // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); + let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; for _ in 21..=42 {} for _ in 21..42 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; } diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index 901ec8bcc09..b2e8bf33771 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -4,8 +4,6 @@ const ANSWER: i32 = 42; fn main() { - let arr = [1, 2, 3, 4, 5]; - // These should be linted: (42..=21).for_each(|x| println!("{}", x)); @@ -14,16 +12,18 @@ fn main() { for _ in -21..=-42 {} for _ in 42u32..21u32 {} - let _ = &arr[3..3]; - // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); + let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; for _ in 21..=42 {} for _ in 21..42 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; } diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 9a646fd9939..de83c4f3d63 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:11:5 + --> $DIR/reversed_empty_ranges_fixable.rs:9:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:12:13 + --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:14:14 + --> $DIR/reversed_empty_ranges_fixable.rs:12:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ^^^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:15:14 + --> $DIR/reversed_empty_ranges_fixable.rs:13:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ @@ -43,11 +43,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: this range is empty and using it to index a slice will always yield an empty slice - --> $DIR/reversed_empty_ranges_fixable.rs:17:18 - | -LL | let _ = &arr[3..3]; - | ----^^^^- help: if you want an empty slice, use: `[] as &[i32]` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs index 561a35625f0..264d3d1e95a 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -4,11 +4,12 @@ const ANSWER: i32 = 42; const SOME_NUM: usize = 3; fn main() { - let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[3usize..=1usize]; let _ = &arr[SOME_NUM..1]; for _ in ANSWER..ANSWER {} + + // Should not be linted, see issue #5689 + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); } diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr index 240188cbb46..f23d4eb0f9c 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -1,28 +1,22 @@ -error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:8:18 | -LL | let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); - | ^^^^^^^^^^^^^^^^^^ +LL | let _ = &arr[3usize..=1usize]; + | ^^^^^^^^^^^^^^^ | = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` error: this range is reversed and using it to index a slice will panic at run-time - --> $DIR/reversed_empty_ranges_unfixable.rs:10:18 - | -LL | let _ = &arr[3usize..=1usize]; - | ^^^^^^^^^^^^^^^ - -error: this range is reversed and using it to index a slice will panic at run-time - --> $DIR/reversed_empty_ranges_unfixable.rs:11:18 + --> $DIR/reversed_empty_ranges_unfixable.rs:9:18 | LL | let _ = &arr[SOME_NUM..1]; | ^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:13:14 + --> $DIR/reversed_empty_ranges_unfixable.rs:11:14 | LL | for _ in ANSWER..ANSWER {} | ^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 7b6dc7b33dc437a59330ef3f5426102ca60fbf51 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 19 Mar 2020 14:14:52 +0100 Subject: [PATCH 0182/1110] add `unnested_or_patterns` lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 7 + clippy_lints/src/unnested_or_patterns.rs | 407 +++++++++++++++++ clippy_lints/src/utils/ast_utils.rs | 525 ++++++++++++++++++++++ clippy_lints/src/utils/hir_utils.rs | 10 +- clippy_lints/src/utils/mod.rs | 3 +- src/lintlist/mod.rs | 7 + tests/ui/neg_cmp_op_on_partial_ord.rs | 1 + tests/ui/neg_cmp_op_on_partial_ord.stderr | 8 +- tests/ui/unnested_or_patterns.fixed | 41 ++ tests/ui/unnested_or_patterns.rs | 41 ++ tests/ui/unnested_or_patterns.stderr | 267 +++++++++++ tests/ui/wildcard_enum_match_arm.fixed | 3 +- tests/ui/wildcard_enum_match_arm.rs | 3 +- tests/ui/wildcard_enum_match_arm.stderr | 10 +- 15 files changed, 1314 insertions(+), 20 deletions(-) create mode 100644 clippy_lints/src/unnested_or_patterns.rs create mode 100755 clippy_lints/src/utils/ast_utils.rs create mode 100644 tests/ui/unnested_or_patterns.fixed create mode 100644 tests/ui/unnested_or_patterns.rs create mode 100644 tests/ui/unnested_or_patterns.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index a4a184480fb..adc945a6944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1683,6 +1683,7 @@ Released 2018-09-13 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f8923b2660..9809f953d67 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,6 @@ // error-pattern:cargo-clippy +#![feature(bindings_after_at)] #![feature(box_syntax)] #![feature(box_patterns)] #![feature(or_patterns)] @@ -12,6 +13,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(crate_visibility_modifier)] #![feature(concat_idents)] +#![feature(drain_filter)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) @@ -319,6 +321,7 @@ mod types; mod unicode; mod unnamed_address; mod unnecessary_sort_by; +mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; @@ -836,6 +839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, @@ -1073,6 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }); + store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1433,6 +1438,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1616,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs new file mode 100644 index 00000000000..2723af03c0b --- /dev/null +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -0,0 +1,407 @@ +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; +use crate::utils::{over, span_lint_and_then}; +use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::mut_visit::*; +use rustc_ast::ptr::P; +use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::DUMMY_SP; + +use std::cell::Cell; +use std::mem; + +declare_clippy_lint! { + /// **What it does:** + /// + /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and + /// suggests replacing the pattern with a nested one, `Some(0 | 2)`. + /// + /// Another way to think of this is that it rewrites patterns in + /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. + /// + /// **Why is this bad?** + /// + /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// if let Some(0) | Some(2) = Some(0) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// #![feature(or_patterns)] + /// + /// fn main() { + /// if let Some(0 | 2) = Some(0) {} + /// } + /// ``` + pub UNNESTED_OR_PATTERNS, + complexity, + "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" +} + +declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); + +impl EarlyLintPass for UnnestedOrPatterns { + fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { + lint_unnested_or_patterns(cx, &a.pat); + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::Let(pat, _) = &e.kind { + lint_unnested_or_patterns(cx, pat); + } + } + + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { + lint_unnested_or_patterns(cx, &p.pat); + } + + fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { + lint_unnested_or_patterns(cx, &l.pat); + } +} + +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { + if !cx.sess.opts.unstable_features.is_nightly_build() { + // User cannot do `#![feature(or_patterns)]`, so bail. + return; + } + + if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + // This is a leaf pattern, so cloning is unprofitable. + return; + } + + let mut pat = P(pat.clone()); + + // Nix all the paren patterns everywhere so that they aren't in our way. + remove_all_parens(&mut pat); + + // Transform all unnested or-patterns into nested ones, and if there were none, quit. + if !unnest_or_patterns(&mut pat) { + return; + } + + span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| { + insert_necessary_parens(&mut pat); + db.span_suggestion_verbose( + pat.span, + "nest the patterns", + pprust::pat_to_string(&pat), + Applicability::MachineApplicable, + ); + }); +} + +/// Remove all `(p)` patterns in `pat`. +fn remove_all_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + noop_visit_pat(pat, self); + let inner = match &mut pat.kind { + Paren(i) => mem::replace(&mut i.kind, Wild), + _ => return, + }; + pat.kind = inner; + } + } + Visitor.visit_pat(pat); +} + +/// Insert parens where necessary according to Rust's precedence rules for patterns. +fn insert_necessary_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + use ast::{BindingMode::*, Mutability::*}; + noop_visit_pat(pat, self); + let target = match &mut pat.kind { + // `i @ a | b`, `box a | b`, and `& mut? a | b`. + Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p, + Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)` + _ => return, + }; + target.kind = Paren(P(take_pat(target))); + } + } + Visitor.visit_pat(pat); +} + +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`. +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`. +fn unnest_or_patterns(pat: &mut P) -> bool { + struct Visitor { + changed: bool, + } + impl MutVisitor for Visitor { + fn visit_pat(&mut self, p: &mut P) { + // This is a bottom up transformation, so recurse first. + noop_visit_pat(p, self); + + // Don't have an or-pattern? Just quit early on. + let alternatives = match &mut p.kind { + Or(ps) => ps, + _ => return, + }; + + // Collapse or-patterns directly nested in or-patterns. + let mut idx = 0; + let mut this_level_changed = false; + while idx < alternatives.len() { + let inner = if let Or(ps) = &mut alternatives[idx].kind { + mem::take(ps) + } else { + idx += 1; + continue; + }; + this_level_changed = true; + alternatives.splice(idx..=idx, inner); + } + + // Focus on `p_n` and then try to transform all `p_i` where `i > n`. + let mut focus_idx = 0; + while focus_idx < alternatives.len() { + this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx); + focus_idx += 1; + } + self.changed |= this_level_changed; + + // Deal with `Some(Some(0)) | Some(Some(1))`. + if this_level_changed { + noop_visit_pat(p, self); + } + } + } + + let mut visitor = Visitor { changed: false }; + visitor.visit_pat(pat); + visitor.changed +} + +/// Match `$scrutinee` against `$pat` and extract `$then` from it. +/// Panics if there is no match. +macro_rules! always_pat { + ($scrutinee:expr, $pat:pat => $then:expr) => { + match $scrutinee { + $pat => $then, + _ => unreachable!(), + } + }; +} + +/// Focus on `focus_idx` in `alternatives`, +/// attempting to extend it with elements of the same constructor `C` +/// in `alternatives[focus_idx + 1..]`. +fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) -> bool { + // Extract the kind; we'll need to make some changes in it. + let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild); + // We'll focus on `alternatives[focus_idx]`, + // so we're draining from `alternatives[focus_idx + 1..]`. + let start = focus_idx + 1; + + // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`. + let changed = match &mut focus_kind { + // These pattern forms are "leafs" and do not have sub-patterns. + // Therefore they are not some form of constructor `C`, + // with which a pattern `C(P0)` may be formed, + // which we would want to join with other `C(Pj)`s. + Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Dealt with elsewhere. + | Or(_) | Paren(_) => false, + // Transform `box x | ... | box y` into `box (x | y)`. + // + // The cases below until `Slice(...)` deal *singleton* products. + // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`. + Box(target) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Box(_)), + |k| always_pat!(k, Box(p) => p), + ), + // Transform `&m x | ... | &m y` into `&m (x, y)`. + Ref(target, m1) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| always_pat!(k, Ref(p, _) => p), + ), + // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. + Ident(b1, i1, Some(target)) => extend_with_matching( + target, start, alternatives, + // Binding names must match. + |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), + |k| always_pat!(k, Ident(_, _, Some(p)) => p), + ), + // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. + Slice(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Slice(ps) => ps), + ), + // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post]`. + Tuple(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Tuple(ps) => ps), + ), + // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post]`. + TupleStruct(path1, ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!( + k, + TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + ), + |k| always_pat!(k, TupleStruct(_, ps) => ps), + ), + // Transform a record pattern `S { fp_0, ..., fp_n }`. + Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives), + }; + + alternatives[focus_idx].kind = focus_kind; + changed +} + +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`. +/// In particular, for a record pattern, the order in which the field patterns is irrelevant. +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. +fn extend_with_struct_pat( + path1: &ast::Path, + fps1: &mut Vec, + rest1: bool, + start: usize, + alternatives: &mut Vec>, +) -> bool { + (0..fps1.len()).any(|idx| { + let pos_in_2 = Cell::new(None); // The element `k`. + let tail_or = drain_matching( + start, + alternatives, + |k| { + matches!(k, Struct(path2, fps2, rest2) + if rest1 == *rest2 // If one struct pattern has `..` so must the other. + && eq_path(path1, path2) + && fps1.len() == fps2.len() + && fps1.iter().enumerate().all(|(idx_1, fp1)| { + if idx_1 == idx { + // In the case of `k`, we merely require identical field names + // so that we will transform into `ident_k: p1_k | p2_k`. + let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + pos_in_2.set(pos); + pos.is_some() + } else { + fps2.iter().any(|fp2| eq_field_pat(fp1, fp2)) + } + })) + }, + // Extract `p2_k`. + |k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + ); + extend_with_tail_or(&mut fps1[idx].pat, tail_or) + }) +} + +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`. +/// Here, the idea is that we fixate on some `p_k` in `C`, +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`), +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), +/// where `~` denotes semantic equality. +fn extend_with_matching_product( + targets: &mut Vec>, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind, &[P], usize) -> bool, + extract: impl Fn(PatKind) -> Vec>, +) -> bool { + (0..targets.len()).any(|idx| { + let tail_or = drain_matching( + start, + alternatives, + |k| predicate(k, targets, idx), + |k| extract(k).swap_remove(idx), + ); + extend_with_tail_or(&mut targets[idx], tail_or) + }) +} + +/// Extract the pattern from the given one and replace it with `Wild`. +/// This is meant for temporarily swapping out the pattern for manipulation. +fn take_pat(from: &mut Pat) -> Pat { + let dummy = Pat { + id: DUMMY_NODE_ID, + kind: Wild, + span: DUMMY_SP, + }; + mem::replace(from, dummy) +} + +/// Extend `target` as an or-pattern with the alternatives +/// in `tail_or` if there are any and return if there were. +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec>) -> bool { + fn extend(target: &mut Pat, mut tail_or: Vec>) { + match target { + // On an existing or-pattern in the target, append to it. + Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), + // Otherwise convert the target to an or-pattern. + target => { + let mut init_or = vec![P(take_pat(target))]; + init_or.append(&mut tail_or); + target.kind = Or(init_or); + }, + } + } + + let changed = !tail_or.is_empty(); + if changed { + // Extend the target. + extend(target, tail_or); + } + changed +} + +// Extract all inner patterns in `alternatives` matching our `predicate`. +// Only elements beginning with `start` are considered for extraction. +fn drain_matching( + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> Vec> { + let mut tail_or = vec![]; + let mut idx = 0; + for pat in alternatives.drain_filter(|p| { + // Check if we should extract, but only if `idx >= start`. + idx += 1; + idx > start && predicate(&p.kind) + }) { + tail_or.push(extract(pat.into_inner().kind)); + } + tail_or +} + +fn extend_with_matching( + target: &mut Pat, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> bool { + extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract)) +} + +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? +fn eq_pre_post(ps1: &[P], ps2: &[P], idx: usize) -> bool { + ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. + && ps1.len() == ps2.len() + && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) + && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) +} diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs new file mode 100755 index 00000000000..69a7b6c051e --- /dev/null +++ b/clippy_lints/src/utils/ast_utils.rs @@ -0,0 +1,525 @@ +//! Utilities for manipulating and extracting information from `rustc_ast::ast`. +//! +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. + +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::{both, over}; +use rustc_ast::ast::{self, *}; +use rustc_ast::ptr::P; +use std::mem; + +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. +pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { + left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) +} + +pub fn eq_id(l: Ident, r: Ident) -> bool { + l.name == r.name +} + +pub fn eq_pat(l: &Pat, r: &Pat) -> bool { + use PatKind::*; + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_pat(l, r), + (_, Paren(r)) => eq_pat(l, r), + (Wild, Wild) | (Rest, Rest) => true, + (Lit(l), Lit(r)) => eq_expr(l, r), + (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), + (Range(lf, lt, le), Range(rf, rt, re)) => { + eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) + }, + (Box(l), Box(r)) + | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) + | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), + (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)), + (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => { + lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) + }, + (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + _ => false, + } +} + +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { + match (l, r) { + (RangeEnd::Excluded, RangeEnd::Excluded) => true, + (RangeEnd::Included(l), RangeEnd::Included(r)) => { + matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq) + }, + _ => false, + } +} + +pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_pat(&l.pat, &r.pat) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { + l.position == r.position && eq_ty(&l.ty, &r.ty) +} + +pub fn eq_path(l: &Path, r: &Path) -> bool { + over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r)) +} + +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { + eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) +} + +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { + match (l, r) { + (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => { + over(&l.args, &r.args, |l, r| eq_angle_arg(l, r)) + }, + (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { + over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) + }, + _ => false, + } +} + +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { + match (l, r) { + (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), + (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r), + _ => false, + } +} + +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { + match (l, r) { + (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident), + (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r), + (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value), + _ => false, + } +} + +pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { + both(l, r, |l, r| eq_expr(l, r)) +} + +pub fn eq_expr(l: &Expr, r: &Expr) -> bool { + use ExprKind::*; + if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { + return false; + } + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_expr(l, r), + (_, Paren(r)) => eq_expr(l, r), + (Err, Err) => true, + (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), + (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), + (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), + (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (MethodCall(lc, la), MethodCall(rc, ra)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), + (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), + (Lit(l), Lit(r)) => l.kind == r.kind, + (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), + (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re), + (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), + (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), + (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { + eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) + }, + (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt), + (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), + (TryBlock(l), TryBlock(r)) => eq_block(l, r), + (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), + (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), + (Continue(ll), Continue(rl)) => eq_label(ll, rl), + (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), + (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), + (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), + (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)), + (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => { + lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb) + }, + (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), + (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), + (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { + eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + }, + _ => false, + } +} + +pub fn eq_field(l: &Field, r: &Field) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_expr(&l.expr, &r.expr) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_arm(l: &Arm, r: &Arm) -> bool { + l.is_placeholder == r.is_placeholder + && eq_pat(&l.pat, &r.pat) + && eq_expr(&l.body, &r.body) + && eq_expr_opt(&l.guard, &r.guard) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_label(l: &Option06LKjls84(Xe7{K^M;ib818MvT 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[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

efMD-gRRAsmQLI5;@0ld(h= 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 z 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 z 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⪻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

6q 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�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 -keZTel4hK0 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

vIE5j0Wu7Wvee*(yh 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 IfW~D20G~Yz3;+NC literal 0 HcmV?d00001 diff --git a/tests/ui/macro_use_import.rs b/tests/ui/macro_use_import.rs new file mode 100644 index 00000000000..6490a2107d5 --- /dev/null +++ b/tests/ui/macro_use_import.rs @@ -0,0 +1,12 @@ +// compile-flags: --edition 2018 +#![warn(clippy::macro_use_import)] + +use std::collections::HashMap; +#[macro_use] +use std::prelude; + +fn main() { + let _ = HashMap::::new(); + serde_if_integer128!(""); + println!(); +} diff --git a/tests/ui/macro_use_import.stderr b/tests/ui/macro_use_import.stderr new file mode 100644 index 00000000000..1d86ba58441 --- /dev/null +++ b/tests/ui/macro_use_import.stderr @@ -0,0 +1,10 @@ +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_import.rs:5:1 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::` + | + = note: `-D clippy::macro-use-import` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/macro_use_import.stdout b/tests/ui/macro_use_import.stdout new file mode 100644 index 00000000000..e69de29bb2d From ede366be637657280ca506c5bad0f41f96c47340 Mon Sep 17 00:00:00 2001 From: Devin R Date: Wed, 4 Mar 2020 07:59:10 -0500 Subject: [PATCH 0189/1110] collected all the imports and names how to compare macro to import path add more imports to test --- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/macro_use.rs | 252 +++++++++++++++++++------ macro_use_import | Bin 2816064 -> 0 bytes tests/ui/auxiliary/macro_use_helper.rs | 55 ++++++ tests/ui/macro_use_import.rs | 12 -- tests/ui/macro_use_import.stderr | 10 - tests/ui/macro_use_imports.rs | 37 +++- 7 files changed, 290 insertions(+), 82 deletions(-) delete mode 100755 macro_use_import create mode 100644 tests/ui/auxiliary/macro_use_helper.rs delete mode 100644 tests/ui/macro_use_import.rs delete mode 100644 tests/ui/macro_use_import.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cd258c7b506..89f55986f63 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -60,6 +60,7 @@ extern crate rustc_trait_selection; #[allow(unused_extern_crates)] extern crate rustc_typeck; +use rustc::session::Session; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; @@ -1060,9 +1061,7 @@ 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); - 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 wildcard_imports::WildcardImports); store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); @@ -1080,6 +1079,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: single_char_binding_names_threshold, }); store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); + store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index cf526409374..4c89647a574 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,10 +1,12 @@ -use crate::utils::{snippet, span_lint_and_sugg, in_macro}; +use crate::utils::{in_macro, snippet, span_lint_and_sugg}; +use hir::def::{DefKind, Res}; use if_chain::if_chain; use rustc_ast::ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{impl_lint_pass, declare_tool_lint}; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{edition::Edition, Span}; declare_clippy_lint! { @@ -20,82 +22,226 @@ declare_clippy_lint! { /// #[macro_use] /// use lazy_static; /// ``` - pub MACRO_USE_IMPORT, + pub MACRO_USE_IMPORTS, pedantic, "#[macro_use] is no longer needed" } -#[derive(Default)] -pub struct MacroUseImport { - collected: FxHashSet, +const BRACKETS: &[char] = &['<', '>']; + +/// MacroRefData includes the name of the macro +/// and the path from `SourceMap::span_to_filename`. +#[derive(Debug, Clone)] +pub struct MacroRefData { + name: String, + path: String, } -impl_lint_pass!(MacroUseImport => [MACRO_USE_IMPORT]); +impl MacroRefData { + pub fn new(name: String, span: Span, ecx: &LateContext<'_, '_>) -> Self { + let mut path = ecx.sess().source_map().span_to_filename(span).to_string(); -impl EarlyLintPass for MacroUseImport { + // std lib paths are <::std::module::file type> + // so remove brackets and space + if path.contains('<') { + path = path.replace(BRACKETS, ""); + } + if path.contains(' ') { + path = path.split(' ').next().unwrap().to_string(); + } + Self { + name: name.to_string(), + path, + } + } +} - fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) { +#[derive(Default)] +pub struct MacroUseImports { + /// the actual import path used and the span of the attribute above it. + imports: Vec<(String, Span)>, + /// the span of the macro reference and the `MacroRefData` + /// for the use of the macro. + /// TODO make this FxHashSet to guard against inserting already found macros + collected: FxHashMap, + mac_refs: Vec<(Span, MacroRefData)>, +} + +impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); + +impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { + fn check_item(&mut self, lcx: &LateContext<'_, '_>, item: &hir::Item<'_>) { if_chain! { - if ecx.sess.opts.edition == Edition::Edition2018; - if let ast::ItemKind::Use(use_tree) = &item.kind; + if lcx.sess().opts.edition == Edition::Edition2018; + if let hir::ItemKind::Use(path, _kind) = &item.kind; if let Some(mac_attr) = item .attrs .iter() .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + if let Res::Def(DefKind::Mod, id) = path.res; then { - let import_path = snippet(ecx, use_tree.span, "_"); - let mac_names = find_used_macros(ecx, &import_path); - let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition"; - let help = format!("use {}::", import_path); - span_lint_and_sugg( - ecx, - MACRO_USE_IMPORT, - mac_attr.span, - msg, - // "remove the attribute and import the macro directly, try", - "", - help, - Applicability::HasPlaceholders, - ); - } - } - } + // println!("{:#?}", lcx.tcx.def_path_str(id)); + for kid in lcx.tcx.item_children(id).iter() { + // println!("{:#?}", kid); + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span.clone(); - fn check_expr(&mut self, ecx: &EarlyContext<'_>, expr: &ast::Expr) { - if in_macro(expr.span) { - let name = snippet(ecx, ecx.sess.source_map().span_until_char(expr.span.source_callsite(), '!'), "_"); - if let Some(callee) = expr.span.source_callee() { - if self.collected.insert(callee.def_site) { - println!("EXPR {:#?}", name); + // println!("{:#?}", lcx.tcx.def_path_str(mac_id)); + + self.imports.push((lcx.tcx.def_path_str(mac_id), span)); + } + } + } else { + if in_macro(item.span) { + let call_site = item.span.source_callsite(); + let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = item.span.source_callee() { + if !self.collected.contains_key(&call_site) { + let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx); + self.mac_refs.push((call_site, mac.clone())); + self.collected.insert(call_site, mac); + } + } } } } } - fn check_stmt(&mut self, ecx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if in_macro(stmt.span) { - let name = snippet(ecx, ecx.sess.source_map().span_until_char(stmt.span.source_callsite(), '!'), "_"); - if let Some(callee) = stmt.span.source_callee() { - println!("EXPR {:#?}", name); + fn check_attribute(&mut self, lcx: &LateContext<'_, '_>, attr: &ast::Attribute) { + if in_macro(attr.span) { + let call_site = attr.span.source_callsite(); + let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = attr.span.source_callee() { + if !self.collected.contains_key(&call_site) { + println!("{:?}\n{:#?}", call_site, attr); + + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + let mac = MacroRefData::new(name, callee.def_site, lcx); + self.mac_refs.push((call_site, mac.clone())); + self.collected.insert(call_site, mac); + } } } } - fn check_pat(&mut self, ecx: &EarlyContext<'_>, pat: &ast::Pat) { - if in_macro(pat.span) { - let name = snippet(ecx, ecx.sess.source_map().span_until_char(pat.span.source_callsite(), '!'), "_"); - if let Some(callee) = pat.span.source_callee() { - println!("EXPR {:#?}", name); + fn check_expr(&mut self, lcx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { + if in_macro(expr.span) { + let call_site = expr.span.source_callsite(); + let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = expr.span.source_callee() { + if !self.collected.contains_key(&call_site) { + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + let mac = MacroRefData::new(name, callee.def_site, lcx); + self.mac_refs.push((call_site, mac.clone())); + self.collected.insert(call_site, mac); + } } } } + fn check_stmt(&mut self, lcx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { + if in_macro(stmt.span) { + let call_site = stmt.span.source_callsite(); + let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = stmt.span.source_callee() { + if !self.collected.contains_key(&call_site) { + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + let mac = MacroRefData::new(name, callee.def_site, lcx); + self.mac_refs.push((call_site, mac.clone())); + self.collected.insert(call_site, mac); + } + } + } + } + fn check_pat(&mut self, lcx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) { + if in_macro(pat.span) { + let call_site = pat.span.source_callsite(); + let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = pat.span.source_callee() { + if !self.collected.contains_key(&call_site) { + let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx); + self.mac_refs.push((call_site, mac.clone())); + self.collected.insert(call_site, mac); + } + } + } + } + fn check_ty(&mut self, lcx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { + if in_macro(ty.span) { + let call_site = ty.span.source_callsite(); + let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = ty.span.source_callee() { + if !self.collected.contains_key(&call_site) { + let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx); + self.mac_refs.push((call_site, mac.clone())); + self.collected.insert(call_site, mac); + } + } + } + } + + fn check_crate_post(&mut self, lcx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { + for (import, span) in self.imports.iter() { + let matched = self + .mac_refs + .iter() + .find(|(_span, mac)| import.ends_with(&mac.name)) + .is_some(); + + if matched { + self.mac_refs.retain(|(_span, mac)| !import.ends_with(&mac.name)); + let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition"; + let help = format!("use {}", import); + span_lint_and_sugg( + lcx, + MACRO_USE_IMPORTS, + *span, + msg, + "remove the attribute and import the macro directly, try", + help, + Applicability::HasPlaceholders, + ) + } + } + if !self.mac_refs.is_empty() { + // TODO if not empty we found one we could not make a suggestion for + // such as std::prelude::v1 or something else I haven't thought of. + // println!("{:#?}", self.mac_refs); + } + } } -fn find_used_macros(ecx: &EarlyContext<'_>, path: &str) { - for it in ecx.krate.module.items.iter() { - if in_macro(it.span) { - // println!("{:#?}", it) - } +const PRELUDE: &[&str] = &[ + "marker", "ops", "convert", "iter", "option", "result", "borrow", "boxed", "string", "vec", "macros", +]; + +/// This is somewhat of a fallback for imports from `std::prelude` because they +/// are not recognized by `LateLintPass::check_item` `lcx.tcx.item_children(id)` +fn make_path(mac: &MacroRefData, use_path: &str) -> String { + let segs = mac.path.split("::").filter(|s| *s != "").collect::>(); + + if segs.starts_with(&["std"]) && PRELUDE.iter().any(|m| segs.contains(m)) { + return format!( + "std::prelude::{} is imported by default, remove `use` statement", + mac.name + ); } - for x in ecx.sess.imported_macro_spans.borrow().iter() { - // println!("{:?}", x); + + if use_path.split("::").count() == 1 { + return format!("{}::{}", use_path, mac.name); } + + mac.path.clone() } diff --git a/macro_use_import b/macro_use_import deleted file mode 100755 index 61d3a827f1f77ecf6d8dd69590a42eb2cfb26354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2816064 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%0DiCt;U3T}bcg_6w*=MY+mKX(#=g2`z^Qop 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[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

6l!wgDv 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 tht&6wB3#E6R1AiS@#JX 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 z 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%0A;?_D0j;DP>l8qFq7!7(_u4e( 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

efMD-gRRAsmQLI5;@0ld(h= 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 z Ybw4Ps8N)Y)voc6r_h)2a4*t(S$%nDyk5;XoU%(6s^dDNJT5u4p4-_idIk| z1>%D+$Oy)8KTvPqCXf-n1u+MSJqL-c8^p*E8w3s}c$*F6e8zCzP&TM;zJ-n;Q$eOf zR-;R7bMPk^cd)w$3LA5{ok;Uf#dD}0zB$ieWT z{#2-WC>1`kAfoZYAk_d>46>XtJQ!41lyI^#z=T1iw!}uhbKlX0z{#T-su>gu@R}D{ z5Rp8(ph`i33QZmo7lRoY7(>8uEHwct4++|O*A~b^uu>L93{uKU9e`?8 z23J8FK~4nqyiY)dK{cJkMsUv?CIpHWXwUmJR28VugZ8`y4B_>}b*L;T%3w}F5ra4Z zQDZ@}!g(@as0ucfgSl(u!&6N_NVT=r|Vc_tVcpS#aARG>6 zYkq**ehTcdb)jqw_rS@To7a_RI}V^_g9au@gB)5=%Rwm)-f=({M9zB$po&4$sEpxR1lOd;Lbr7ggXb6dcg?>l;au0L5*IN zF=J3}!_iKYdH}Tr(gR}SWM$y{#lgz(7%B|vYD;Vc4^F&>3W0(LI*EfO1R9(`69R<^ znh>bid=E7XRB}O!O;jOpv571MFE(LCXj0&dUcmyT%00O%^a6&hM>H!5eETy7|K~gF>q1!># zf?5gCgpMo((~cqr(T*rbK$Fdk;h>c%D4kYFDTUH$g$jWp1LS`Yh6sW4$p)|&zzOsL zm=>TDrKnDqcFiN0<1V9)hhzN8P2ZFK;No!Ud%)lWJ%E$#^CfI=s zh;|^Tz=h`^kmEoYB#3YzgM@V?BSUs1xKNe29L>nU90O*<8!)rL1#D;-8$)XtxZQ9F zsnG#xm-BB$5&{*e;6dOO=z`!u;3O|rh6`qpL16HbA_jA?Cqa400Kx=CR|13y$}Ch#ig1`r2*90as_2*dXvK_uTZaOZikGHkE_JK^?IR)z;)CYQ=f zRt5n}s0|V?Ss5Ha9CRB%B?5*GAVHW7nhi+FoiY4C7#qU}NFajRD;-ctP>ev^E2u)? z_6kf0R6D}kE69R~_DVn0Ku|P8+bi%Q(i0qw#h_++c^7CEK1dM*gEM%Pj^}U-s*s2% z5AP|Y?Kq&OjK~VGLsQ}z8K%U8(}={Cct!@61PI$Sfsr8<%8pKAWav%;i^J2=C9ngw zg|jg{3kRp8Nj@MiL7cG%>X3$&zW zfh9OzK?`dxfSFjPW>FT@fcoChVj1EtNG}d;K@CV1yjX^*!&*XsQX57B0SUqq2m^RQ z4TBZfouCCZ24E)2+%=@G057CT0Ly|G(o6s|!E@IKtRM+hfZ*IUXcz{=Js?4tdr$%h zB?6r4kmqTn!Kn^1 zPh$h}6?m|<0L%mrwl1)Ncn~sA1LB|;0ifa@!zmy^OsA{@XMn~CHinH5rwH6ZWPlv7 zQ}~&Ygy5;=#=%>)P&RHjaVFhOPN0SFURrhb4hL1n6g z9Y`BEJ}1~gy$foaJpgggy$eda7~Ta5!oABtS(PVX4{{ASp*et=V22ghLmWo9P6mzT zV>l2bh;SgN#SB`fxd3cFckfeHh6`XO7ig6xg9F4WLC`8q0}uy23_uAD!wQff%nIQ% zDU1xCQXrej;N@Qt=%(s$u1Gcpt4MH4R0VC>f+p!qMv%98w+13o1Sk=KHjNn~DT@T{ zY-ttbvNmF4NN`|a5C?4*(=%dYnBV}}ip1p(+ARr@vE&8GKrH~BM#vy_S%`swS7Ix| z5)ezgl?Q3dJ7fjLsz^45TVQ8$gGMYrIDni79UJ2M#$Mud5ahs<}Cu(6T%x-GcsIX4ONwTgpr}=2$U0ZkC9>8 zJ*bv1j~E$L9z(R$yklfo{0^$hmxYO;goO!Wi#0bBLn1d5c+;OSCl3>Y0S`n*LY0?^ zA(WR1yaDqxFB8LWC_93WiJ^lJBL0_;i9wei!agg&#PC-D!oDZS#K0#6VQ&^5jNAmaPQm>Awe*<#{M3~u6J zap5FICWa10s1NokGBJDyaX<%V{Z?XP&{hVkl5kgHVklREu(zr(LC2UFB_8N9F$m~E z#J3wVF+4Yfu=$Oc80?L}Y~e>nObjx{OyHOh&bDP@SZWL9$lEh9MB6hlNGxV#6fU)A zVpt90bTKjtOF1wxgg8KyNoYASF+@3m4U;(R#KiC!%Jy_-VyJWmiwj?NVPfEPWnz#3 z>F#i4VmJiifV^?Sor&RxJ6M%Op$8MgLJtW0y$2J6yeEX+>dC~g6~Y#-3TI;25)QRG zHin6zHwL1pD4&U8Q9gvdvY3hCW-)}lw2_J7Y9oYwu9b=5Un_)d+s4F@+Xi7Lb}%ta z>;SWcRr;A2y!)ZaL}n@zgU?im1n)v729t#l3E|gknHYrDK{?+xFfqt%ghY}=!U-mZ z2`9i>Brcp_VqiE4Vf%e%VyOEJVZUQ#W{_iJ2B$trab^ZjaR}Q;jhP`y4Z{AX#>}9n z4q=A}GBdOXLfA|}%nU|B5cbA=W`>9P5O#MlGsEs;2%B#@GlSi92z%0WW`;vh_R1y9 z@U)S)jG1BHGKiXstC$%WSA*HYSJpB!{94Nl$unH*m>FEwF@qC_#H=0645xNLR6X3y z%)qgS8Jx`|+N4+*wn?#oy~C!>!eFKiVP~eYFw9Jau%D;0Fo>r?*tVrC47sIXws3C+ z3&Z9L7Kn8(Dp(klDp|nR3H#TwFqGCpWmea+FgyiurZF-Kt2VGOcsD?08XH&`c7r&e z)bzW9g+aR$Y_ddC4-3Pl9tb;iA`8RRiD0&{=VTU!{K-(=n#nM#60w3q#p5uoj8A%UBrB zLfFD@m$NVkuYf2Lc3#24Pz&OKweMzO;NAnVQTW$>76yd_P-P_tSQyrUI3Q(}r&$;l zoMwT9w)F!RhU^CrTY4U_FzkV{ufJenV1Ee~7k_n3_-6$*N!OZ{A=VnKN@A}I zE5lnCFkARqAS=V4K!_q?i6B;npdeOAByxqaG8lwHWdcK48M;9nke`l6urj=jfXZ-1 zvNE_tLi9)k$Fnjt$Ae9l5X)d?aLa(O{VP}*>MJ1Z*VU{H(lrn^b0;f4UJZ_pvgt_e0owwz4w3*$QS02kvKOsMrtnYr-K`hW0~HPRB7;hV{oF z9Er=vSQ(g)gSAM6A7^D~hq7;;VrAew4Hg&v@rIQ_?k&Va63bZF7_PB^DtJbTyV7h7 zyfP4WmJA!iEGYZFG8+TG3PjvOm5m`o6~gA!W@E6_2D2GhbwNk{nt-}xj7SHO?f~_~ z7&#Ef_dWyFzKk5E@Dq6FgVydc!p{+=FXh{Wp$HdPoekC^2)`)CKNl>}1~>G78(6>tu4UaE zus|AIVCgcjz#~tXJAQ8j3&8K#W9tBK*Wo}MSS+LsYBM4oHM_tWEPyzK_@EnD0CW~G z@AxIq45 zus{Y}fc+s@pbakYf}aVL91y479z6;cK%7x5@eC{=3%`rq(|{SI1$41DI zma^i&Ds16Fcn%_v0asy>%>pt5e#9|b73jzXM#MF!46H|47#P@ovx0BoPL5?|P==nl z&&+;?g@K_h7IacBgBJT4E(V6x5Do(yNCtYI`UVd+21W)B5Cw82_y(j8pl#@EARSOe z(A$wP6$!UmFfkmpU}BKn$IK}FVLKCph-SH{dBSI*2JYr@DV9B`MJVZvQz2H6+PjKVk5SQ*69Ss7$mnHU*F zK)%wQ4Z4>|6y$maQIPW(g#&M}Fig0?0x>JrfR$mc0V~8YeREkE&dr5z7}#7y7(jP^ zw!|Typ$;-=MI1PQR2ab_bQZ#cgpdN{!lsosLFXECfG8{>1XTnMAr35xgqhE55*m^W?*-KD6l(Ln_zJVNRWer15=T3!3+k5H8WuD z=vocVGt)8Lu^VDDG&~+aco26$^Fs^hfOHNH5CwKeNC2`spo&0(pgU=xB_;=m4-Pcp zeXAK5eyxVNW2OWnC|=|f(ZjQD$P0{mjZJ z+;70d@ZW%mLG~ppqwo?dCI(?^sLaM~ObiO!p`50FObnm?L6xoe&%_|Xzzosj$-vC8 z1H=KDX2rt8D<9Cvrx`)K^6vcAr^>3 z%Y;}Mo`N_adkUmk7#>JNWzNd6FxbemKpg6>%)+ou8OjmYU}0#}fO4K`urP#aLiGsi zurM^~KxGW7Sr`^nLuGhsSQx5npfZB(EDVk9P??qOEDVwzP)=wE3&Rl*2jm=yyDSVH zccC)Y_gENK--B{I@3S!MybtB*Mpg#bCMaie6DtE#Gn7-< z%*yZ(!~rFjiS4Wmj2%z|ygOJKc7Zq`=VW%WGTi8d>UrJ8$`I8J<>XIhWwcLSQ+FNLS>>BvND_maX>}~EM{fczZk0Q%VJiB{04Ko}d3S62zILCri%0m@_1Q{8yC=%YT%Ea(p719)7V3XGc--G=XbP`S8| znUR6*Q3Ti>tm*J*QDFebi)K1k3FDPExA0TSfkz@kVv zIFgBBawHSDQN_TP5DUI?x);M88zDAB!{Zu+2XP0ijRU$)g@c3N2h!r_xQyZss3MRc z2R22*%5h8#$#F1u98O^Z)q0{C=;2|RfgB!j5FW%G&;#^|4(6Juup}gQ{nR*Yzz{dY>+1FDNZ&9E>Pxs0gAE5+29!4j^V=F5H~?XiXjv1 zUWf~!kx`Kejxi7gjxqIbSYiw$$icyZsYtjkhlycd4lJZr)PnDMx6ecmsbq-FPL)l1;bH4AkKg~N;V5AaG)s!ARgv(8Iwl6e zdYC&Z>cQ@a$wCjD28hj2cPxeQAb|t50^J>;TWP@l1{X6>MIb?BcYyd{MZ%lvnHWAp z-64Ew6BC2bW>}2<=>ZqPAK{MTfE2+Z+2GIsWqd9M21^JJ;wEV6>bC%-f&)Z>Bg}dM za)dz@fdm;DuqYBX=w)Ik?1hAlaK(HiPR|-7j^Py~j?5uugq};MSP+~EW~>N~zuSn0*@)L%=pj?~{S;#}07rEzLnM_h&+EhPq=Pga>g4H20$2T!pX# z><*|Rs5?MvQ5`ERy_1O{Vke~j5muCCVMvr^0T;f)%2QbxQm3+ld%+BB>%W6tD4vTR zQr5Z1A(a5(p}6prKe*iwqQI7anvI-Hpe_Una&T~9DiXf*or$5Ig&ExL7q(?#W~c#i zK)vy~EX)jdK^#zTJU*S3VLqq>4lWMe{xC7@;7~e-;oMgc2SNjsKM(A9h;w&9deB!^ zgKiY!08wD)g2E0Fu~0=IK@JWqiiDf~Ffr`^0}D_YW`;N!W@zp1@fYl<=sff=tcN%Q z>Zm0U9*U!6PJzp45Jk{Yn2Lle|1vRb`wMlHVLA&#UOEfdQNmx5I1Fr`|AJ%rFNO;h z^T7cHb)g%C2XP@Z0vCZUL*w88QCM6ERRj$-Ohv*H|Ckt}LA@_aW=7%vEX)j!tk40N z$c>;ShV zK@`C-#8f1FoQIi#g%^_AgrBOiFes@(Vwr($xd_-%*FcT}Uw;U?br{sJ`~`6a)KT(< zNPz=&l-6``egIJf9fhe#_?-wdgSIHdQ4EG#B$yct7&(;U3(;e#8DcfmAuAy~21Ae= z4HzLgQ+5s5B_ImbC5E8kHJB=B8pBj2JX?yH;f55{DQtUXz(Mj7>DG+CXD1y$wR3xl0lbIo5Cd?WC=7VFUz8D@Fpa5WDU|0;X85%1mAUudWpb6FY zA-F~cQDApen=>P|AEAmsf{Yv-n2LnM7BDkRTL5#%k!9fEU@w7>QA1)ys{|4pEZ~l* zKZFNy1~kCK!Q<8(APS2!po+lGV8EhCSavxxL)vm?NE=^$HG;#yRwfC)8+kU!O(-dL zKg1a-kY4H&2oK^YsJHVC!6_C*fgJ_PB#5K}RRj{`;NZYiB)nCUh2fJVtT5T826l&X zDS8R#UJ3~uh&ys1JQR0m%YfYhq6oSJQ<3m@H5LXFb(lLYX@W0{Uyb38^AMY%;qe8+ zgSZ3Q;rf@%2I{qgC@k)PDuRXwrXpc|Ef$6xEtoqP^}y~hE<+EG@G|7^sDbcM+#!3{&)A?kG(G$IEVzJ5UPw`w*L<;lWZ4b```O(5RP~4K7+i6c%?t z6+y!TQ<3n76c&cJDKK|LfEFBZC^?tI^A0o*r$cOpx}yieLvhDS&;$=72Z$o*4opSD zHEE!RHVcESC8$rlIu9H#r$O#O$uI9AHbdPZQh^j6(5O#@#0!YR;tr@HXn0^M5;n|d zVaUseg-1^T*d4(Y=;@;zVl&hob09oOctEYN?E{bMfGC3Qz*Ho>zkr2-v5*Dah8JF1 z$ina(%3)ypQVdQkFEAX%Qwa_ZsG|%aJcy&9=^*7II1_^?ERKRIf(8zzB4M`@7KY{$ zSl|Rr0K22061~8m0U1D6l&~42(hzB*?+R0isY93AavQVK_Je zGS?+sw2_5j^F|hE8_sPy*io!i=#J8?LJk~12oJ?kzaZmZAPQ_BH_CtkG;lzI92^{& ziiAH;XJIg&0c+DLE3$$n0(&vsu@Pc3G;pp#co26$`={sw2F%Bi$48-xK!V5v1|U8- z1cgHtSs5lOvVwbE3~UEBgKjfb60Jth(U#T7;SmSnLEHhY=|F9L4y1m;=Jiu0=23!fPSn0cj7^KzJzb z(6wa()x#hP93G%j0}&ojcYp*rI5;pB3D2%&Ww=obopom`Yhh(*;85C);g0(do1x*s zQU`Vw#2pQguEj%0c@LtnxC5#P>JCgr!jD>58RT1G?wB_b93IYf@ZJCgr!iOiaGB8YnxI?&q5`x3Pz`CG{9dd3pYa$l| zgLWBcT{a^F1DmuqXt4qNv2a#~GwVSi!#+)ufnn=C76uLhcF?&ig5hio91;?({;Uij zof8chB|5@c84iWBGE8(}WMJ!z=U|ZFU@sD3W&jP}a4@i|`Y_MafrGs`o*g1@z&=@!f#Gie8v~08`{inOh7I+s42%}+^W-@h znF0)Kr!>K{^tKJiF~3ujfgvBP7Brr?N0Wh}3Cd$)KcvaPFcrdM zU;`f#Cv($-%%T5YGYf z$~;X5hT<^LP2cRG;FrG-j#n2g28M$mQ3c_K^$ZM~)-y23n=>*BudZTZcu~c~AnyWN z|8YkPWC8ohXPgW#K#CdI1Ji%gb9HdjNyBX*ce!h z*%;*KvJ3bcg0@vaf{nr666{j`1xT7frvZU9^G`s^Y2et1;^5%w-owgpft7(lc`B1A zKg(f+ouG0;vz+8fZqW3XWipyS{{?gf{QV4*~?dIKvqh6;#!uFq>(83fqCDc@FJXcj9&02>2? zj+_XRqae{Y*@}(fHbj%aUu@AQT(gpa;mAs`QHbb(WFmHJHU@udunYM=wIMzSK)^5wja2}#rKx-Bvu;7*nx2<7dIJXAu97ITijAIPH zVavwA35f*$m=1(yc)os<AFYAG{cmYCwY8KRH2VEYa0kc`G z3|dfGMEF*0V1TFql?@G0Y0%}gjNzB;*chbjAz`3)o0VZAR2F13D6fHwNSGif@vCWY zN`i|>m>{Sm0~e9}dpcPe=0f$mgU(3Q;N;++JCl`RF;o!b2YFE0GGh-b!vQvE+2VYd zmEk;81t>(oWy?M`G{IFoAl=ZW5jadCg^RI08$&h30|GL53l~U&nq|)hDJSQ$^H(7y z6L^BkMG^$n3HnnQCHWK31zQ+7`6qt^y8}#sRaP-d@t{{J#<_~%-~y{+05!WA7{I~B zz`!7Yo=)|rFmf<32#0NfRs_4)5%~d>cp1YV*|RZdIe_C!0Mx>S84F6~p!j0oWXOYv zfx8yrCJt;2HBj{yZfp$9P+6FF5jh=V{wxPJhDT8KQP`TOAZ1_+ASKT~2Q~&bM~Hhw zv6Vc+7q&7mNNfWqPlRV6agpK3#;^gZ8C0i(&1HaWb%%NeDg-GOcR8{#FgZaiGrf$Z zScJDAK(1sAw{&7-XoRQ-34qN4@3{z{?Zn3L7$PI!I~QRRXqyMB%@FlG&TI@J&Jdd; z@u~;47a7AVoY@!-K-BZ6BB=-U#6afre?Tg>Km~?CKcghS^Fc(h1rpR|W#iy)M@o8N z!FOyT{K$zEB&dIynFA$}f(6kNDOeCak%9!#6DcSg!V)RyraSaPR-m7e10(T53t6-{ zc<9W=py>h*Id~jEdLIrhYz!R`8G*O!5kAN4eL&)1hYK46gDco1fg+?Zg~u71%}TCp z3|SEMXfi#nYz$W*GH^G63OvT}pRQ~Sj&2Y;K&=OGl3`$AfS09k^^hvW%Z-g;5=1@! zOSIYo9M5;q1Pv0II0T}FKmiOf8^kh*Vv^?1KoSJSi9r;T8fF#ZOlcJo1*t+Hmcy!$ zEpBWKe<0ovaKcta!@Uj3xUBAM48HE*VB%-rfQUL+slxvYDLz3(quF#ONr82%5h}rg z6PQdH7|>fq+3xUef~5eow1U{W)twDil2{r*Wk3#N4FB)W#^C7zc8{e4icE?JymAy~ ztYT#FtOEBD7&R||dYudm3}?YjfiE#^46{H|XNBr#vob93U|^6x$03xwo0Z`Mh`mZ& zFl;v~gM%jngZWlQP^%lhogQL*VJ~MJ~nLlP_nBdL8;P{M@k3aA+E5i|} zwVC$!LEztYRt96JVo=^uo6aI8a3qM8fv*l?0w|ZM&0yhS-~vVI0dEEdbx#%@ z0Z^P;Bk2RjEf@CjV;&^ayfP;Aghyx4e3qFuw z7LZ)W%CH7%EI34)nE1hAzY8je6811buu53iAAzbw343Hgc-W%|B8B~Fs0k=xe;FzW z4pCUxBMZXAUJ@E|V3n}2M;3&JJ-^l+R)*J5V?oVTH6IQhaD<=;*0F0dfKS%5gPIGD z5pND2e#gD63{Fr%kXu3e`AyISHPab6_|?$`=W}!LUt>enuIbM$EdXjgBAaQn7u2p| zVBq)L%gW#iwG7;v_2J+V$T)&3sQHZpB!_%45+sw$#j!Eyw1PAF^x3Qo0>0op3N9rQ zKx}9x-{6bL`45f)!B=#q=F^DFCB?3^4 z@=Ic4SOS*NOn^!p1c&6iBsK<@WRMP_M{igeE`Wmn6bDxnXqd>KfkEAblLK537=Sp= z%pBl?Ai*E0AeaE+zzTu`AP%e`UGLDsj zk%N6(BLf3yg$5%h`#~6!i~V2&J45zNRt83H;k%6t3=&NY49dNrwHk5}91M(%>?dJL z85r1#mx7JoF2v4oX(ni5O1O3@1H;ax3=GN>m>7kfH!?8PZ-jE<%9$8ul`}zPOf6X$ z3N2Y6oL{ai3}$W+4g*^T#7sg?W?*1tEP+mONK9S`>6xldVU+M$fne`kgL3?Jf!2tit*tfvJ*7xX|CLrz!)6`bHA_XB7U3R+@1u)|7B z(2)Y50)qjf8dOe$RX4CB%GV1B)eZSz#Zc9twjWqEXi^DQ8iURg05=Q}2R}m01~v4+ zsuyq|%>KXubBSU=0mN*`armH867ETe5U7|{oPeSVR8FI*0u`7N=~ax72I@*ii5FFj zkhbPZMv0#-jF2j6C8I=nDCx?+yI9QU2;glp3gP$fNqp;mM7KVA}SQz|{GcgLs zoM(Z^I59H{=U!l8SftLt;3on~{nPUpL34E=6QNV6X?ctg7YMND!k7~5m3fQ|;3-lK z_6C@!0((~;BLiy-hVju(iZ5We- z{UwYk!2aPp3j=sfg#==9RDr$Bm=mH+gZ&3gtpWRg7}G+S`vMDt=LHr9zc>~~VVA2c z4EdJ;8kiUaqB$6RFR?KSD_mq@0O|Jq&(0{k>mmz-&LtKG-;ZpJ z!W%EKFw9fnWbl2+4zd}vJy`fTwt*d-Q*1t5of=U^0GbQuz{zV1wn!YdP58D1u`GWg!&WfV?GVuiTi3p1ne)g)F1 z;bc~@r3`F`@)#LBIM}T-*&%_=fCzUE_P9JohRivvpy~J}d5n-k)q#B{jOoY$N)bJ& z9Ef49LU8X&CW?(AAc>X1w}nMOY7VH%fn-fkJ>@%tMVw204l6@|BLjo)Ocp+_O?y}w z7C166_!Thf3NGEl%J9IEfk9K4k%NKj;SN>?1t$guzcxl`t}}aB83LRb82k^j@CfeT z!^+U$#K550DaOIT!1ZhoE5ineLRG<=ATvO63%NKL82I}F!f_{=E$qnn9Aqlbwh!-bJiSaCiRL*RTUM`jHZgU=c$=l*3T z2L3Bh&Z&dU48IOCL-f?1VqsW&iUqCY6 z84DqM_!!u(D1Zt9T!z?#7ggYpW#!q6bPxcf#k}5wjp3mO8-s-h11mqKw5%r^LzpKU zgT*X~Ytl>%{4z`o7PA;7=Iv!-xV{&{K6ikLf&Cza?Wo7hP^HHVRK8R)Uf`tLDIuPXd@QGhV<~)MrQxFNd=gr~;3(wlWARYvRhJWk~WqB5&iP;)(@D|n~#&tpJY&AI;7 zk08th3zjgkFrbKnf(CY52G0^CGeC~DGv?tF{|9jrhy;mAY~-y)_fsMd0|QSIl2dgU z7#QqKc=-1q83+=yf5Rk>a-A<|5KCes%56jtA@E_pAUi=AD2N40N)nML85pKObzXortw55D;rqSVpfbEGmasA)OM-NwB|<50cp_Xu^+Y)F zJSa&(Tn`Ym(8K(flj4`AAXB02s`{15+w{0?Kco13Ke63T<+9{2+^G@ zSQ!$aA?hgyvJj#alxQtpum~Wpa{y&IrX)r^242v8JP>t|ktxN>+w!s3a`eXf{9v(!kZAjW4_e+VY5jp&cp-TGIim24P~L z+5uJ#B8$PQK@>4?HOO;;g<%5JM39F-CD7t6tSDmO5@;HlAh-ld=R{QrE`g$u1mPu+ z;}L{Pupqb^1c^?8+Ky2I&4EgQJq4`>k;S0ZAhM7xxEkEBg_U6`RI?+r8f@T16$6(* zpfm4b3PCX=0lA|GRR~-Ht$-Q_iXwP5h%AVx2G>KCf)WI@8Z3Rq%CH?O465It)gZDE ztQtfSgI9yh8cYm!8cd)Tl|=FjCWd)0AnZHenHj`?FoVUTe=;*n`w3y|{9+$ z`HN8V;y;4z%E$s&|DTZsuKqAHLOh-YA%4(?h2f743)rp9LFEGzg9V6lsGga@r-7NlLXeSBxMwFbgTyXoh|J?BEDZTiSsk3@KJoO zQQ$tz!!Qm82GK*_3=E8-=ew+U2^NGyPe`yZ2uQLp=*;0^6qc7{VMqsYa(Nhq)xB64io94DbTs%Fg`2!s7*2vX zsoadh46|4mJZ6CgXLuNeMc1%ELdS@WQ8;c53nX;TF)<1Yt%n-m$Hpiey&h`7b0$XN z56Y|%15UCq3QMnHg%|(|-jp@05CiSX0|Q&L5-VsNc~TBL!;!VD zpw21->m<;~o<}?f&RoI3CZhvN=-4v@hu{WSp7|Wi%D}*gT^S?+sVhL26@uz&#&B&z zHikk&HU_y|_^b{@VuB$X!!?M60g8@ChHMNPMqnKdC=wY)Yz*tb5)z?atPB&qSQ+FN zGfK?zVr6&;VjpG{{sLnwFfvMf@MdMu@qs9E^I>J^1+fn^3U7q56+lb;-vooUGU4zM z1MBtm>~0 zAQ8QgiDBVFCWg!tj0|ix2^ z*4&LO3~cvJL6OaV+!XHCGp5j8DjX6Af|(e81T!%tY+#glvznD5nv0Vmp$%G!OKcEh zVVJGSz>r|Z$O@t*4vMpY2QCvVKpk(^Z>9_ktiMee7})a7z@-y|83RK~EO>GRwIoR3E7#P4z0rqbeP$uXuFxER33=FIfEEpJUn=HZ3d1k=?9ghO<>e#&*WH-lB zJ;)(h!nZjf$M}a9F)}jPuCQWYU}9oN({p_@D+3b){19Z}rhSYIXZJBOK=eEU-BrSY zuIDqv9?*G2plwYIwtk=+M=+G}Zvi_8cBT%4?e{=zuGWSqTWkPzb?XVpeFb0_FtEP0 zU|?YTZ2{H^+PVVr9R~y4lMHOrApQc$fsS%U@(cspPKf(Kav;}oFd!Y?%MWohNDkyg z4hHz?KMZVb5SN1FK^AV;~)cry#^Dbu!xtDBUS5DI`+tXlLG>PJuWenY@1t3{(C= z4T$1kWN6@Egt(1Gjgdi14a#B2U}R9sfO44UGcst-XM}jpYd#}GF^B^)?ZG`p2DbZ9 z8Qc4e3~3+^NXA}?i6K>q38H6~5);E=5Cx(s$*f;4B~*4 zy{Thi5UPhNV`*ez&}xJ#OK4<$__QMFuVY9Kmqcom4$(;4eA`HDJ%>bQ=ocQ zPhnxW0^)%5NKIv7aF_};ddD0VhI@0M%2?*IFzC)@fy6@CTo#5J5C>#5|6&#f)5TC_ z`HNW?W`H;#(+;d)VR*IzDkHX%g~1+#K{B)UurM6h1C_Da%fgVl7izTVK^6wM8_ z3DNzbtPD>>p~~38SQ+%gSRwua-FaFG;(+{963NOiD-x<~dn7Bv0}uyfntL=WLw+|hBqYo&&ca|X&cfioi;)qz zQ!*)=k%2jfk-`5E3!`vBJ|hFjDE}H3M&a}Mj0|=Kj12xcER4e2iy0Y=OBfmaFMyPl zASp90XJlAX4mF^)l9Ay_B_o6XD;7p!-aU*A3nMug{0mqZg&h||Ch`2`SQv!`Z!;le zu6$!+aQ)5%ae=WsGs6;jW(Y_4HZw!ZZ73)I9y7vilbO2m_qOSP-5I5l7a%#5tORxAv{>k1heu7gCfnHYsL>KGX&)-f{phcht>9}8h(cpt*V;GfOJ z$RPX(rfxnnqcBGuBZC1*-8W`N;W;o(51AQ-b3&LHrhqhUVP+Km0W)DfGb8KwX{-zq z4WcXz>qJ=?{I4@Iu(E;d7qntwaI|7!@Q-0;6lU>YVKDJvVerpmW)xnp19f^MGpM^( zf1I5G+^?Dg=~wLqr8Ln;pp+*17TmA;4DMGo7&0)hi2n6vU|?ln4c)^4X(0>8?qOh9 zv4?@dJdKf2cq8cYxxEYw=9?KAg%eF#7}l7wFqltfW|Ua=n1$ijV-^PUgNzc~PgoeD zpRh2Pe_<4sea^yA`kaNq{3xjJ$o4%By!#l`<=eg+)O=uI`yK@r0V;S&R(- zL7dBCjKZARj0{cLj0~C`VvNFqIgAWbau^vjgSi=n=jAXm2{V~ZFWb`>!)XmW}(3ZE-tWUwy=)%9YG!e=TN8Qejf>0*q+#Z8P1%*~7pnxHtMuz00j0~DG+>FA%k1{g&9AjkA z1WkAzJjuvlbc&HdQ<0ldIN=l{!&VRnWWW~|CWb^-CWsz>HYSDyHYSLk1DZ?>?pjO? znz7uB!l7DF&I)cu;fGpG46)iw44SLC7$xTDF)=*XV`9*ZU=&`J$pi_425v@SgDfV7 zrCCgnAc)FiVz`*c1PN-%LMDbeg-no;It1e^=4KT3Dq>=|Pyp2 z6rSwI%)sH#42d502xbPq2&mEGk<1L;kx-)<6OcF#Rm==0tC$%y4R{%aW1E;69yBpS zV(|xz1B&^&W@d)Z%}}FtT9_HiTbLO%#aI}HPp@ZYklw(|plQOwD7<1HGlS%QW=NuR z-_Oi22gCs-%FS1p8SWf{I_Jh!W(J#U%nX{K9Or(Gnc)D41Ilqe511LwKY+U67K{Um z(xivX4A&n*U2yObGlTDAW^kI2NO;W5uo}dPU=-f@n3+NM3Dl!=pE5JNddke8sV>ea z{QW62L&P&?22D^_bovCf2IRLBpO_hJKSNEf`@+n?_mvrvNmaixGpqq|K>20&S7rwD zZ_pSyXU_spM#2HkEDR?>X-blpQTU-V3qzI*3phYof4HzP2s61tlZiYpqcCd>5@!yK zqr%I`AaT`;g+a!fg+cQPqp(Q?l96j8SQxA$Sr{~H`51+RBUu>Ef;g-KjKU8hSr}rY zSQs>G1Q>;TqM$O2f{d(9PnZ~lBcq{~HSsYr2O;FjGz{tvQmXQ@wmV9SqW$<8Pg_I@EY^)3$*q|~W*jO1H*`YG8 z^H~|p3RoF5TUZ%|1E;exoSM$cpxFZ|0e7%6+}^>;ps540X9p`o&Q4ZH3O~DxmBDm3 zR3>saE5lU~XEhh2a5Xm@!+mZx22C|?M&bY5Yz!qlY>*Tl#SfJMmCkoz98g+G6<}i! z=Z7jgAjrnxBg6(yE5ap0Yz!|z98mobt;oi3Ns$eb;l3)eF*qo(F=%!$F*2|{dTlFY#Y*8V33Z2P*W z3=rF|889$c?-%>dda|$@Dj?|`-6eu_YWw?riGEAs)Z3^ z0K+0i2D?R2PU31thW^!z3|1CwjKYSy85#0-LuJk#W@Pwt7%Efnh>>C4BdCn{J4OcA zcTkyse;65T{z7FIFf%cnV`hSwww;TK;VTyt#L@=}Obl|0P)>{y6T>VcDCdg}6N8a0 zl%p8V#1J12<@}t*#9%%f$_Z9yW@uDrh8R89k(uGLBQt|l4;!O!`C4X%t!tSf_Hb9Q zFnCt5KsaY=SQxl!p`1naEDR6op`4wQSQx%ff^sg+W?>MR1LZ7S$ii@cA(WG_l!am5 zQYfcjISa$OCeinx5`=OlEoU9B#IawhNmEvV(h~Q;~I48!6m0^|@ zl;a!B%FrDQ<;)3TWw;dr<-|s?GR%&Ea!g;cG9~3-dm^KRuNfo5Ofv}k>_a97$wy3JasNk53=2T)M%bX^e0`{{ z4n~PfFkKy>miV+_wyJTwDK7l0`j>TSwS5);q?WK45EdM3<3V!j7UoZ z!H1fNN3$`+N3$^mcyjZvJ_efT1I^$uFvv17fYftv?LEfIAmGfv5YWLb#P#S3x6JY>oGG5b395nF@@+|+fXP~)R5EHU{KcI?RoVN>2Q0WS2 zMpk%HDI>$JObiCDkfoMfpmmoCt_%$N zI?Np4b(a%dVe2kIyHzoKvxi$Amv3&AGcp)~(ux!}BY5yYIKKkw8)jtR6oP%j7Q@Dn z4)V=kF8=FCz5%BdW^N9ymnT>m4!ANf1Q>G*af!cWVi0g+UDXY+D{k_x%79k zG9r4y_u0!;&e!I@Zz?!1Tz#u&71{1^Q8%zuVm)IGF{q&g`*6TAvIOk1~I8A0qoVHkI zhUc+RWvAMaWRkm)IP2#iI1CJ|BHK6_Sc`8lF$h=OVq&;|i-{p%0;uaOyi1Fj;hz>W z#5d8}%nY-&nIRsC(q(3tsSDz8F$!B6A}OmiL{c`{2uYd0F_N;$#z@Lmnjk4#8p+J? zG7@U3XB0C-XB5aDc1B_E7-oj97!ZexQ8>PqnPG7)Gef{+4o24624)7gxeaJ z8QwK8GX%`yU<3tX3zD7JT9E9V+Q!Uqr44GBb_bHO{0<~#dR@#6xm{3YM`j@@ov41 zFLCHK6NBm-CWfrM%2K>oPMG=rc;#8X?%hCJ6T8 zXl90=(GWEsTA3N#+92%CPG*Mtoe=hk*>Lp?46IM9m>F06XEQJ`u-Z?9)m2h%!otewZAdtnt5GKyd!L@ikD?>mQ1B355 zW&tjfIjjs5K%5llGV}{s3=Docj1u5w=my!)W$65%73z@U8gin9pDUxL0BEH;vLIx| zI%pyKglq-|KQ~5ILC`|<10YjC3(@&Oi`iisKv&TFxiL!fg9=wrFQOpc((NJZF zpvgDvCkvxQWDPUJ%^GHgFdIf;?K)qOP$pT)PHl3x9pe!ch8*1*}1Mw>>L@!%@P@&|bpI5N5{6D7>tMl|ikP z6|9Vb_0x4G2I1e=nHYR;FfoL&Ff$6T)njJ(ug47G)S4o349t)?k7Ae^Ok$zRG~1aO zs@tKQLtV@aBHd6<>>Pw12G*%>pv92z+&4@NhHse|vRN4!CHmenF(|xaV#qdQ6mEXV z!XW;L1tO92h=t)Fh-1bm>}bo1khp5g3YWN4$jXpb#0t^zr-+rIub35N6axcWcQ6ZR z_YtTu9LLMdz`?))qCj0K23F?+R>FXIKxC^0d1JI@k&n@uX8QU*{ zdyF+tpc@N4I6xvVHnTDaAKAjnz_gW>A?!W}BLmwj$nFe~os!@yYS@E)7#RMifbR|k ziGcKQfcSZ!J8v1lYn{M13Q831VPs(6%g7LNnNgx`KO@8K{frDDaf}jc&oeQwUtnSg zsbrK8y}-n<9>jjiD6tz<%U@>#sTXEm$O6$D&d4ad;3^A)=QS3F5Gh7R36JY6470DZ zFod{+7YPJcK}%Km@@w$jv*E5WkaHUXPw*U`fXIU<85kG>PjY~d8iJKaATjV+8=#|x zV1nC0JECDn4Jo)ojvC?u9W}JToq+*##E>9plemHh1A`{~Xd#IY8=1ia!-4M^CC)&H ztODON3ZH%k8CMEyU}O~D5Xu5k1R6WJ0v-AY6lP=;zBL0f4g%sx%r;g8@*+Y0 zOa>8FzDS!i_`xeZH^;Csyoq6B2nuIlP1I*&khr18#vq~3#xTc}Q9@0hjUgVy4gd$( zd2?8zy>8CLpkcwp5Tnb)C_E>giQ#QLl(YLI69dmDD98Q`GsE;V%nUJsOpLs)WMFsy)ayk4*{%zgn?RxqEH;lzAaurg3B zZOQ_lV*ss^V_XYPm!O3k;9YnE3=FKGbsDVj#eWjd%vcyeLY3RW{`s252~GIEy(|n{ zds!IjEg2=gr?N7nrm-^AtAZ6B0#(49LBw$oaS3$DCMca~aIhCDF)&nI2Mu*Fu*s-_ zPb>uq350=CI0M^64e$}CAR#rVP%fxR&cQD0#=rnR`j>%S6UJm=H*jNMh>r&kqBy%T zFl5BDF|Y_Qu&(p~?TQ2u8$B2p*uHy!P2J(az%U7~1io z1$!Wj>A)ThV|uWsz?cE-MKERrdo_%iz}^XCX0T6!F$>ri{AOYh28|z8fP(k^Z}8yQ zW&s9>y$tNT1Yiq;*l+x1Vo(5G8^FWBV7N$vnL&b)gS}n?x`v5?y;Xu4qKkvQTY{MZ zys|`qeToD#Ln|n%gxF_GFf-^TvoSCVGe}I)V`I3W$Hq{5fRTZX*MxyVhK2p4F#|*w z1N&7NlZpMlG1LVt>~D=37+kKiGO%#4|A&c6FtDYXFfa(Puv?hGwL8O@Ozb`;3=Frf zu`;l*u*aA%Fo3$Ij2sMXz9tMHp->a3;S3CHsg4W`c}(nqj&NgQ92pqq#<4LlGBHTJ zC}m+#C}V**I5ZowlMOYTz){7)z;;O=l)>#8SV5=wvb{3~yY9O&14BwQIDIm(IY5R1 z^$nOAj{1SuMp+mzLsp1^Rz^F30?e@obR?w)gHw_N0|Psw^9|5R3G7buKm`j2c#j>c z(Ml!;Hfwzb(CNdV1A(7B0%yee?_jIV;8(W`e^|)CpuUKKK@HUW_!`2rm}-$F18DhOJA2;V}0EC}90Am9?r z%CHow6m)aFqX&}!0|S?NFe}3XkkeK$i3#34!pd+Voq<6Dw5**$_@fCE!+lU-q%blH ztG{Go&@|^@P~&D|6kh$3iQ)cBCI+?DER4d9UziwnePLoyyUD~TeEbU&!(R|bkCjna zQI(k?T$PzY?K2Cb@Md#nh7ab<3~C{4jKXpj%nYFx%nWLGSQ&*C{8$*G{8$*&z~k88 zIpNVh@iBU|gYq7zi6I;`lZnB8CKH1i$Zc_Rm>3q$VFE`X1KVc-xB-7L40tWd3>kmk zU&O@lt%wPdZdNxjG2CxrVo-BpWE5W5%*1f3nTbKomz7c2zmtifrIQJgh=RMB7`nTe zAc^R5FB1cQ9}_qcF|f%C!!50Ng6?Wln5B}Fm>7a4L5;4N#Kf=}!~vz;9dno%-h#XU zN~{OwF){p@2eoJCd?tpk^I`TZQiR*{8N(h&=)Ozg3WO|YV2H$!ZS=58UC3tLX@pAWn@q>gUT#&U}WHRWMojQ zWoHyNbYx^$3gYClGYUU-WMpu2Vq{Pg;bas(6UfK_TI;CR$;K#L6wb(SHyo;mIf9X) zJc5xy4Wwsh1S5k~BvemNEF;7BSg10kI7WtPaZqJ<;}{w2;-Si3Br!4sCqtD@PiAD` zPJt@RPJ#Ldjk)T6&QJQ(6?VoC}6D46vK?l3RPn6t{32El>gPM6T6`&#o)Z7D~34|gD zYxW5r&1GUR$YX*8^UZuF2HOHA1~pK0CKo{Fwbc^Y8HKgVm>BxXpfX#_m>7)9nHbcz zvoi|!wJ9qY?3m5O;0sEzpil~0$HZ`E9TS7v zTy{obf%Qxb?dzEs)ZTM43Ttd-Vpz77i9ziK2cvM`StbUib4(0s-`N?3bI(CJt?Z1# zQ!X+wa9?6#Q2Wcy$eMbSi9xvVCKChOEhYvvUJgbE;aj(v7((tqEvUM~1YR-$w!rcM z6T{XAOpw&){+Nm3@?$0jHAyZ;;S0>n;3;*Gb6G)23X;T|RGAqx)tDL7=5m2Xnr5p( zxzo8Ag*h!DbJ*Y{=CFntyutv?Vcp8i(6N=7K@H^EhV4+!4Nyq#VP;skhnYd`9Xq3N z>>*}`>xV#wvoi{}&u3xyH=hL(5PO%fFqki8fdmnUB`ZUdB`bup&61Tt*NPRQtbHeB zjuC9B?=Dt`9lKZ|arqxQ#|W13KEuk8xt*0k&5oT>coq+2juGTh;dov)hQqvUkSJfp z2bop`%bZYPV=z`^1DnRc7NN}uT5S!gUfUrPeh~GiHn=egYRx=?oUT)L8+vj8hj8z0CWbS&nHbcX*cchu?mh)w zV+!^NJ=P~!C!&;08tEVI|GTcu9`%G9R zk&z)k5#nWG;kirICg>s;%nZMEDTX6Ss=cif0Bja5s32?v@98XW{==?28P<} z3=EE-9by^p7#Oa+gK|oY7#SWIF+yadEg2cwEuowvHjE4gwos0ND* z?Mw{n9ZV2sTszLh;B*4QVPJdz1|D{*%y_aAEZZW&&g?A%L*rXWRyzNdfk6u7X;9ex zGiGJ*HDQH>-Fy>PhBqJ%DB5gHSsCh0S)nQBv;iY*9g7b>t6}vZ!fGW$MuuENNVJ_c zWMmKn1u@9#RV$eoey)V3nCMkZ3@cVa6D!ARCI;`-uoUy$8E$nkKC5BnI>Kr>7e0D1hTR|z$k)FQurk;kg!o!Qf`N@8fq@NNX3S=Q z)B@nF`^pXOq*?f!1gioNP7-owWQcW#IH|{-k>NUs4RVs?URH*Ty-+93-pk7H0>lA1 zNpc@6L(D#?lb$oOG3YZvoD>bMFu+c_>MkV4GN`-OH2$$FF}(D`(-8uugkDx^57`AIRmO^ z_*w8|4p`d?VH@KyCI-)AkP;qLqQI9lYxqypqcdpZT!r7;e7s z|3j<{eutq^JLNDd!$S}U6ke)FSQ$VErGn}%iD^e!86F;G1(oX1L!PvQL1#NNN=#2- zV2Ds-XOK=~1os^z4#Nc2GD^IyV`NAJNp514=xbzT0G$pcy@HWJ!r%l0L+l9#2I-rO z64e(O7*8Ocm_X)BDS$3o0o`{59_rfoij{%K;uR}{0aO??KMC9Dk0J)z z=nwBAgBIm5hM$CKhV@lJ!Z6J!VqnbaQ6Yg{zpD8H&F!F{saH zWEB4I$;<%i0H}AcG74J-LUyC7moPF4A53RuV9H=+Q2)lnDEw+JD?{8jCI)q37U;~9 z@Pb7Q42+8z7}O0o7=_svLpHUmH?lJdw^cJTJga78P`Bh{6i)77WH<=J!-zTJA$<8Ru@5>54 zE>Qg?JEO4PTvmqqxvUK8MW8vtS+z_I8XWAGt}-(~;)nhGR5pfBvsoE9Bp4W24Y`;Z z*jmNGdq_b=Rx+eW0d=dK!QE7jZ<&xGLQo3Z2-+f$RALSfF8I3(aFF75`raY6fsbOX5h<2awBNVu*62bSWKZ^JP2zc zs<<0L)@Cp;pjihRUq-hMEC#j?v{J47Drk5L;uMg_te-M*3lv;K6+|2{0#OJdt)DUR z@Pi5&2$z9DoPmMC`Z*I1uR6#zP*IQ|cr;u1fD<#rS0`o$Yi&^H;e{x82fMT=14HUn z@P&d(FeV3h+chXkIRxM_%fNPB3Z4M!5eXm!k^n-#Ko%Ku3N%41hFr{4mdeJkF%^;k z8iQFGq@c1|Ag6*7faO;PxF93}@N(2KGRQ&Ifr7*(Ly&>5H<*<{1u6)QYQfj63>r`& zP`pCJ9$5$$_9$ZDuveHF%*tQ^)eTw&DY20OeBO&YR19P@JTj045s~2!RSNPjJTiiy zf}n7RM+UMWBr*&$m>JSCV3Bc3f&p~uBJRil1uusH2Z#akw`Arefa&AWziC-EV$gc z&jBtRP{bfnz`%Dhn3Z8YRIw5Rs4U~F%wmQMK@tSGc-{cmKCMuw|URUjvNL^28T zzBOb*5%h8c)gTj{Ss89am4YT6BsTIre8tLeA1VYo<`Y`1APd2qh$04YA|xrlhw2B} z4lhKI1rbU4J5(tsk;0SmFQ_2sZVPxJf-DG0%4I1`469R^pe6bWH3EeQC~SU)e#*)a0A_-xf*Ud!7#uG! z@qnj-K{`Mfly%@!!5{$;24xTUR4_;Ygh7Jfpaya1TwNC}Vr5vqh!q;S-}MQUQ=sVL z5a0k&;K&8{y@YL!FfhzJ!oXm?i-l3R@Eilf<#SNZqk9YtF886Fl*bGV#~wpDbN(|h z{QD2(7^*WeOjc)v7@(fc#L$z@#9)1unNj$YGc!Yw3p0cDdS*u9EO%yx^X|+L8PG-U zQJGK~t6XM=`MFS;GyTjAIuoEWg_D^Xu1$u@ly|s!U3cggh7Jf6e}#%#lqkZ3Q=%#uZABy zDT2G60|gg{06f$f7+7EEFfgzl0iBuC)WFQZHo+F$hnpkE%%GzOUZ#RL$Ot@Za@Cmu zat2J00|SGwLk$Ci3mYSY_zHGLVW-oK3`hK-OYz#7w7#YM}nYlcEu`*1EW?&F^W9H+s{>9300L0N|;owh4x=t8WiHMu9a`PhJ zF$~J!l1DgrOp$K!1qF=c5e|M8OHdGlf{K9wEY`}%!5b|NiaR91641&U{(2;fdO)3f zQ&tIH8GVESpe2ivmu+}r-4J4+%gZD$Yw@tYMwltdz`$U#o`WXlQdsI;N8i8stO($_4 zl#MH(sug1g6G#wa2NPHjyn_i;0wdph2XedqBSvwQd+)%4==a`%1<~)l0|^qi_l~F1 z0TFzlyH|r{R0K941t~~OauX{b1Mfj3m7wSfmX~JWN3PN!NtK0#54lnU2}*3_L$1`I zLZC_wmM)+|pvnzZ2z+!ZvJi2V9ca-hMr8*Q1Q#6;^Y|y~BD@Z|C|KN`S%cqaD;b%c=2Y>~2m^cJ7kTRMgsEr9aD0?N+8O5M1DKQ(m>|YSc z2vAlN_hn{5X(fOJB{rf2C{zeJKp{flRsusDyp>=8W`bJ@330@>5CD{xl}Liq85kHOXE2NLyW1gD zf|g}Vb~5wuyCUt603`{@|EzohmS}?D%?K44>}7~ z@;@t!z^fpHey~*xY%C1?tj-8mfzr5S7qcY)4Q+rsuHbW0||no3#DKKRfUojEIj-xk*X?C*(q7aA|NmqO%S60FjA!i4$@i{9SajpqM?&%!88eK}k^(J>P=`(epi65HsI{8jKkE9#j%jzjy|XX=CJj&_pXF z-y^p}K)1?cWLr={3U(FY(iQ3f)OH9+5Hs6?lMJYNh1?DSUBv))6$48026cNRFENYo z`+#n}hNcKmLt64YGY|hEq~rz4^kBDcLlXq2Iesg&<`KAv(L)o2xb*^3@&wmqAh&7- zBidXb7TB%eL@SWQz#w^pSyUdBXakZM7(%XaGjM@YVFO6!CbIw+C>3r1akel^@Xy%` z(huMj6r zzcGtIYB3N8JvD>l0zEZ@1<_M8SP(NcgRUyVNX_6_0~PnkjSY|>*s&;$4R9tQt`@`E zEQhyWQ5qXyQ_-8{U_l~lF?Uebr*AEW(kurTi{NHCN_GI1!5G;A+}@)gJAm3i7})_N zNMv>ZDMe(53!r3ngBi692h~#WY$1@sz#w^(8B#bqfH<(ic>#z6PQLu0J7PY24@ja&H)$B45`S4b1G8dJOLyFN`;_S!2=LyJ2MZYy^zMhAi0GZG_4{~0%|lu z(;+An#JM?D`MZ(EBrk(TMEN;Ic#s>9pglU0Uv*L1j@qDbnIX%-gWPsZ0ttSVrm!!} z`*9PfCV)5-)LfGM#=#G&fFL3a4B&<`Gb4`x$geO#Q1C0ROgsX}UIgdeQYIc0KY|lOB@>STsIrCm5v+0xvjmFA!O0d}$}lhpU(R7+sJ+a{ zAg;v3D6E{z!cdya!XR$S#3&s5l7(UWOBM$4I%Y;;%~vc8HLqA0#FId0-=1V;U^&Ig zAa2b8U0VfmCfH6V@cBsDB5Vv?3!%LU@#m~kLcXqS3=W`;UW+(*8<0jWK(3Yw6%fjI zWn%zc!z>jhEfnd>#&7_ncCn~XpAj4QdPOM*S;1e1Yz&|S5F|^P#TbM{jMx|w7#SF( zoVB>vjo28#E!cC6f?U^(*cdK=O3YS94*6b?U7-FAXi!E_(TI(~0K~hOXPTwM`b_-oYWE# z6fsahNi7xOnP-B~+XwRNGG3mENMfK0L+T!%fF8*I5Gxo!V&G9`A#0G|nHU(P9tp^U zR7?P|j|nmG$|5-lPa?E1^VAbx!A^Mvcg|DYFGUTT*GKgJOb0;H1Xcu&*YGD^6!|n5| zs54co!828<$!rXV7D8sKK8dg~I0Qgvs-B9lF%*C}pqVOWLquGI(gb*>>bn6#5FD{b zICzwh5&&oj95hn}O3l!yL(EixQ#Di+tPqsC8Th4)eyu0K22@d z%v8l&BZ3b!D+QXVnuZjlATe+m!n+(vB`CUv{7lsy9fa3Gy)y7jl@ZdU6j%^6Q>BU| z26W_RripL0A)4GXR0;?!Dp%-fSKT#DuH0C z&Qy8ovN1RWLuaajY}ptZKpfCa)kRxGkbQ zk$nnQ2^rW$o>p)HwN*h~V&n-pP`wXs*dzD!L4uHm{d=UE7bFVqW+G3(fds*gL6iwN zkSMrekK8qa_w*UK`s~;kHUvXws(v8(2i*7t%~T;*8(=}uOcipq0Tu*R8v+;=1ZbuT zR6~3K*+8qAD&(96pQ*x#1u|x;6hasnBtbJ(7|{fpslpmfCuu9C{Hh7R7y|)b-G{i_!AVJI|1WnC9wFxxupoL)0SjX06lH8V1)Qru-F4(B00~m$E!dbC0|OT* zx&lCv51Oe$jwW#P3O$;@g6Po%7Q~Du50HP*qY0WvsXS8!juG@h5%^#jN;H96Lg>*1 z7DSIGupnkMftKHcM^!L#3VJkw{E8V(AWBR6Ryg&I{_hfo7_vAvJ2jnFusfg5%RlHD;=iqYE^cAPJhOLe5nnL2z`T6l|cX96VDMhcrO|t_DCeRY7Qi z5dHl~Qvu*0h0Rnw07VbJnJR-tEJhz*^onh=VZsXA!Q#&94MI#UI1kA^`;p1@=I z2_O!5n1cTtQdnY~eFi$oU?eKGE|%SP(tmg9S13{ot9YLT-n^XR46X z0jMCws5C%=;CMutaR5~%7}*vih*@cXlMH%kgfvryoV>w9WuU=OBcz3KpoTPfrm7q% zd4Y=~P!W@jCJ1pW2il|**sXui8d4Cq&Ou6^pn(lg5hL&&sTB^cpgs*tl3 zND%Bj*i6*{kR{;M%>_!`3K0wplAxI?P^u09aX>@+$f+3|7wD-OEQp?(!Gf5n8C?3I z*J9vU1H}b$`2i9HI~FC~fHMhtV*?}zi3{ZQ7O>(TWqcFfenqLpKn7qm%fW)^wHR0s zvlfHTR3T>vuv&C)fdnb?7I+vLJv)GlMNkrB5J1ijpfVUdQ-z!z;4@WZW(QDpiIE*Z zf{-X6m>ob$5i?Z{pk#(~rV5lT4uDjE<_JL9;sb~So2d$jgiW+!7S5oVDo`?pWu2v< z<`a5y20H~kIfDh!lQUQlGdV|tOhr%5pbcIa#UMzKqIf6JC_^rstw64#PbU#tjH0g6 z#E}|7H33FygwIqVr$$gF0f!)R;S3UlgdmxPGq_U)$~nk|GdPQYat^p~-T+Eb@WMF? zsc?3Pf)vi+R>1@i2Q*U!N|FrGkoE$oSs7&mD)%614ipODnJRUpF-iDL6>{SdHdBS% zc7)GVA-5f=G*h*FGpHtjxDwp<`o_Tz%4rZ0!ZTIrHf#(Dpq2linJVP+2AoUiJyV4| zJOP^7kOa+CA$t+*D$q<7vLC^T0W?zujzXxEGuMk zO7|QqL&G^%@Z=N&o615)&^Gqz^BEZ~*ucm7!RLi3fbTDAg=`H1jrN0gtAH5bVgIa! zjF92{`J9ZxH_x#$n1F`;cXBccm!4;3xB}wXfQ|vnS;`2Sia6THzyQ+A!N3l(Ov09p zfkT2FG_e4-%mTk<8Z-zAPzdN5Oid~ zhwH3h4g>2c(1p`hwcx{lKy1i`)9e|xu+#A{r)(tVR5LKVsb*l1yv8W;@)RQjp9mv^ zvrLJeMk&%E(}7V8y`Dzy#Sve+s-+%IKN} z1H%W9K?+P*bU*kG)jge&f$e-K1L%xFP|&T@2d9UA~$H890xmSHf$g0*lGc=iG~mp z-3-|no`x_$F8M~u9~}NrI~WZA1R=t11;_{@!wwvb5=dbO%0HlFETKM$5uCfhVb>po z2s<|Ld>uIKHiB$I4m-H+>}^opps;g++&d0Bqv9W0?87uLuw4j-r%lkUlaN3OQWMtq0Eu%clC?U|p$dJ$jNt>G|F*2}DhNR7R#~B%%PcVYhX8T)K zhFfo;X_Hl#iGhKQ7qrfX(Vl@#7{=%a@6NYpU`vPq?NGF5U`vBB_GE)Z*g@C1wt?e_ zfz1bevW7ha+l)A71_nm3Tr#+XU|?Vawap|r5dOgz>A_$FIq*b!A~e#4e?~Ge#DZ$y zct%FyphO0SL!dBdVPq7}Phw!O11)h0UKs;})L}-64QY%F-RX=BQWqE**#1Ys6Z>LAJc%77iIUib8Dbb1&c-k>NS1)k zHq1%@$0;a7<4cr?ya$iowFwN6C@p4WV4Ix=caF9(9_N50QJu4-jDcZhIRk?vI}0NN z+mBS@GhYh9c*ugr0|Q$}F5G>mvASMtZ z5F?9m7Y^`r^0|wVA-S88K@yZS)Tb~q?4H8NAj!kTD4cVjmEppDRt8B>3X*-!%CPY{ zD}y9xZ2_A_A>2=(Smt10M=S53ega9N`pLYAfq}P}fkAQ&E2G5HVg`o0B@7HwcNrPj zdTX)yPaRSC*+ULz)nQ^_!0A6&J<9?2pHCeF!_zuwnoh50U~p~#r)l9wO$-cf%?uFl z=}%=~m^PJxK@yY=BF-=}oHzpw63wfO412FagQT6El|h;V5(>har&$?xon~c_lmcB# z`L7usB&$vEBtwuSI7nbsfUsyQ1H+?MSgug)fSbc=hQ}O`B#Jo_dYudmVOIXV5h=Tw#3JY3=Eo+!4WUK zWikW9qA8G6E_`ej1H+uzkmSL@c4IEw0yc9zVF8i^TL2H!m-84H?#+i~ZTY#x2f$8( z0f5NbsZ8K?DrogJ_9Tn5=!OR@JjtpsGcxRDW`q>O_AHDH(yWZ2Vpup!gpuJEsF?-I zt8+ye8FIuJ86-i)IGZdZLzXNfBrz+=F*0<@F@h5_1KVE?@KME}mP8!BKm3Mrd&zEC4rWk_Dc~07;^lBatA;2wvO+ZsW}3CqB$@wsGKLhDeNI!r-$2LA43K z<{_dsftew(UKrYV1=S`U;&5M9TH^5~ND{@D!he++89u2nLTi)rV#IqBXKey=A_pSL zEtH454-{cI^BhPL>^@kY6W*r4$k3z62q}53lo%O`lo=rIQogG zS?v2585Z|J)1>88MuvG)85tzI7#W2lr!z8qnhs6iEf-lC5-zcV6L{BER)#NESs5hj z85xCVU1Mcnyv_ zeaOln{RkQd(;u-iC_RS80pC+rh9ys-aiH*wm0|TWNE`^~{a|I#`pL>5*$ZlT{A6XY z`Nhg0S;xdEJn0uJL)vdv2FW&1RLz?Y4`NVk;7tA?NpKLulfT3TR)%*MSi#AkfvtQA z+?*=VKmbMq2HIf*NrKIRM>)fCXzWa5W)#j`35}3uW=7#FmslAVUWUcNt;NL0!8C$# z04wK&*_JUfWG#cn!Ozu<4E<{uA!(X@EhEFEwT$32EzGcvk)dE6vAd7DnNs2doVGAkKUiM&ak6V?5rlGDtpUVHDo+o|WO!2UZ5j zZ!C<$4BuE8K7M0mkd$R%WDu@8#K^Gk5Y(&s=deX0_6ix|{+$ITfK!(T=Qk^j(=vGhM9L&!^L$++kz3q#&77D&mc^qYm@{cjd% z$tZuD_z1;WGQuMiUNUa`%E%z~4H}^$>`V;R>`ai@QR8G{xXsA~DH&D&voP%b&jQJa zz6`7kHyEICz`?@GFpmWq2L`OH47*t&aUlF$ij`rpG_+*=EzQbsS_WD&n#-~>aLd6; z#s&A`K@5rwoEZot2@Ya-2KxM-h2i7}7HG+s@C0to6wqiRu96WX2{s2F<=W4ou>&d@ zZC^km1XMEK{=~wt;xjA`);=aa4se!?@Hl{%jMC2-8S0-w<6zB8Mh2%>(30`OD@KOs z*U*yj_B%!f@AuG>G4~TA1M_ES$vE*dBZJHrXvw(X3nK&jS7^ytEeDMQP|0X4&kBiA zaLM@f4I_i)TWE2?^npOwK}$w>*ump~`y(TR)F(y;Nl?ic`Gtic^D7I3B)DWW{Q)-= zls#}pEJzX@A#g*TenJfem5fXNurTcY%fcWDDj9|Tu`uihaX=;GN^Vw$Lp;!uFog}uFs zvpWYHOhUA+g^i!EGL%1oI9AyED=S0)R|toJb&nqdgTzrk1_pM21_r4RMv0PG28NTd z3=C4%kQ1|6r9em1XvQ%x%#LSZkXpnjajT1g!Ka6TK`Mb!B8rC*!d}G4z#y@-hk@Za zNG60)Vjedm!)IStsKwqum=pTNj4Wdb8Z zur{Ma_(VpAITINff(02R*e)OHk-=mg zqr|Blj0_ArA?&g%j0|h8K-i(b7#X^Mf!Ul5(36oXK?9*-Nm*DG|e_~=d`3WL%?GqD&!)GQ2lUGcP!so4+8FZ|f!JZP{Z_NzxVHY!_#1eaE zhFkW`U~><+Ff)8}fw23N5#k>*nHkiwAmWC-2=T=em>F(PfQWAkW@WgP0I@nagq5K$ zgq6W$J0qjS{V-OBY72$F-Vx2GBL=!W@3n(#V9dxCKJQKnGp7iS4<32An^%| z5|_5KGO+AmWr&;r-SQ=z3e(lf$SAztl!-ykjEN!g8WW@NS(q6h8Sz<63~jTZGU~6P zCe8(2jJE@7UXchAJ0EM&Xm0 zEDYvZEDTkPLAn`OKo=xNJ1{UfrFk+iuyZ(F6JlUsVs!dj$icwQ;Pl#!fq|8U<84Ch!G7#tRPurWX`SY&2kYGGht`02sMz{wm?0$R<=z`)Gn$;JQ@ zX5e74e#6R;$i@Isrr-%u2A#!ZVAe(`18u>X!p6YBV(ZDq(8|WZz}CeE9-yvaV_=x+ z0WPmVvxOj=IXEDu<$+CWfLU6JFb#C==_`Bx92UZWaeI-KuEd~aLDI5$8 zEUGdL44{SHETGeCSFl5!eH~TfBZNj!IrfqRs_Y-CGEOgeAb{#iR!#;6r-R0z@CFB& zju%Q;m?KojEo5SNXu`k%zQ@u9e2*o>!|`A(8(<#JLTCX+s5vLp9c^G`aDVFTXM#os z*xn_mvg;9sfUbiv=VD-B0nO#D1vz&MCj$dJlapo*2Ln3`Sob5a?ut@qMBk}nMK+tk z8^uqd^(eA(VA&Hey${Z@AnUaO%f5ig^50=VmW=|-a+E>stu|*ymaPEGYQSW@R-^cM z8d%l?Ci@xH-YsC+445qF2zN-*1^f3BSQZ|Zp!kN!x`L=@-fRra)(cR4$>M_&2A99W z{K4kI4IT&u%}v#0fbOzp4+jNuHZudG00Ya03}yxYZ8LRF@YP_`zM znIW2=fq~_c9)S;}q{b{cSVH5J~-vt>M=KHcSFexSU;qtf zGO&KjV`7kC$Y)~k%4cFoS;xqxHH`^$ayjVc0Ffk6sIUu6V`5N$#R9tfj%OMZgPkAv znp-xhX`ubV;J|@SWlFH$V1b##!0J1liGeM6I_S<6p zVHt$Szy^|nhB(-E4hA-mJXGi_$U&ghB@8SPzHAJEybKI13=3Hq45K+1SU|ae#WIeSp&Qh^0GX%| z2=YBgIkXgSM3}hJgN@-LNWX0$8^e7N^ zl!GV>m7!Rifq~`Rdsc=kq6`dd>Z0&) z$7m>WIC!yv&tzv{Vfetxa8!zcfhGDQE5kMk1_qYikDwtO1_qX^pI8|pWuS3$1Z*`t zFhNa2h}B?cKLX1>fH^Fc0mV}cAz&{166hls%-Kd6up;FWkCzqklg$MRTflSBFXZFqJ)M= z8%omB0m~M^LgRZ0OqS)`9TtXH@(c_tJ8!WvOjKlGU;*70cwYe;L8)Mk7S+&TY-DDH zHU(KKL)jRnC^Im)xgYm{HToxmRl|cd`4KF_Sfm~?Fqo-84c&#RtPG)S&O-)U6!BDQXN1Ebh{b47qC1_B^E9WWd0( z!Gn!KUjs$d0@AQ^3quLFz%W=5!x9z7#-OSV@?c^ZsC)CYj+KE|n}LC4Zv!g>OFRby z+eSo1H8Bj_xAM&c=|T#lYYJD*WAFgt0NWgPKqbENbyWfM`A%|R#w_4Ag&lZW{4fAdJZG>993mu=r@77 z`aY_%R|sWObXgdd!<4bbpoD>73_J`#<%F3T)HGdGWfll!jxlTuhfNq5SUyLwFdTsy z8;hzw1EC%?cJu(ItOZqBKSG(w0ValDFlDPzm2E>Pd$ga4f!7r3$V;fo?jn?3e8|F} z2~+kHRT)bx+{-B!85sg$$`oQzLPi^*O!hJ(!xWe@cT{CT2xZqdvNBvUWnf^j*~H2a zX2!t4619nyK?uYEh3&UO4hEL)O{@%zMH~z)9kFZ-Dn%R&9=V%X8QgPYK{e)_ST=@; zA`S*OD1S{X8-unP1A_-BmfS%EBo&=Pb;4DI6aN2XWyrT>U|<0)RPV87U|`V%EjqVi zU~o9Si4|J*^DsCVZ-#NW862XJIN<64@%au}SfTp1Wx8Nn_)f~o_Q zZ6Q?~SoRUB>|2D(zQwUIq`ELLu)OnUVn}m=rXQhr6o1Ob!+mnXi;dw8NWFeM8-t`P z1A`~%I$ai!vy5FC7+Bom*%&(97#KXj-U^FnV{iv4U|>0Qg_Xh6je&s$Bw^*oz~BI4 zK)fQrz)}~_#=z#uz~BMudAWDSgJPZcDl5Z&4+aJYQ0W3u$?8ZGcd48fY+Ua2Vy{LzZn_86)I@zlaYaeEg!t}99mCRx>cLOiN&6nE#A{!Smu%76z6DmsuFvo-r`6fG(4cc*DTJ7XJp`Yyh>MIUv3) z2xVgcbzeajKTptNWMG@F#mHa)8L|U)X`#x%ObG@SQ03d;%*en3imDB6j0`NGl%3|u z2pP#GwMnY8V(8*g_E=+yNc} zMji%n02L>YRKSd!Zy6!M<^`GTU}Rw7m0@6DQ(|UdS@)EIA;tj8=mRl8*KD5kU}j+H zd&9uc7zbtS1Tn%-F)-+rFhjI^%wT3Z*sPdX7$hJH!LksApz0s05G*Rf!1kL3t`IB>Q3#e5K$s}Pz&1sbg+T>kV!k0G z1Jr6zeqdo?U^}G?mpz)wf-YMW&jOmP2eskfeFNV$3YsbaGX)q}KzC4!WV0}^x#YnO zxpWC`#SC301{NL$misNt43j3YFtAKaVPlZq$->}y?>7qr%lu0$47+x)FtDt(VPr_w zU}a$Gc+A33pao@!>aa4fWqPoJQYpy(S}sNgX#SiGx`~*Bfki)%nIXuVm4QvOg_WTK z;>E)SOfXM?Dj60Q1{RJ~Hil)JSs6Syt63S`L8*v=WuF!k!zWfY29|@mObpkh*cce% zI6;HV#w-jB9N-&kWf%_TvokE+#KaH`YcMk~Fl_K(fmCRqL)kc)FEl{9YRuvA0T`A$ z`RoiV6BHO2SYGC{GqBnSg7&qsfxN)X#=yYAz`(j&2wjN%e*rtgQP3!(8zUq8q(Ugi zosp44K^p8F2GFE9V|Xp-j7tL)p)Qc~I2V+$Gq6>&GcZLla$YK9XIN0h&cI~P$O59d z7@!(Jv+a!GJFD0k{(z*t891ZL*%{`QvokQgV`Sf6&dvaGGLsb}Bew%oiymmNQaE2V zI|B$aX)^MFt{M%4%Km3!U|`Z`6anqvV>sZ>!oc)HiElb+=Q1dqJQ-Q|JvkT{zCl%k z20~e_*#&N~Ffjasih;H%vpPu%*l;i~Fv>u@3ewM}BrIrvCdS?mx?TvZy$h-obe0gS z7exCMs2H+#WHG4re=H0P8=y)-rvtG1a)6E80u=+zJFw{rgIxp@LpBmw3}Pe$SpQ+D ze$Yt_tO2rM{l}nU$oi4RAo>MBPPzb9st;O&5va%u3M2;)76zD8o7b_UiN{ASDy42z+Xpx|X)EGlq_g@Ium zR1CDcn01K|ILJ0a#gN^GECz8K|2Y;0hSyM~psc~VT3X;43j@PVGEsPAT zQ;OLcI5^lqTP6cJK?Muvf?{@td&TSwYzmB=6HC|`&X+*gH%r(V#7iM;g;I8gbP(H| zk+Y1ntI{A zRqPBP%;7Jv544U4Dhb-j&k<=MGLwUW!3HV@I_ZTeR-ge*h~+*9e?Gbxc$E)>Z~{~- zxLFy#tcslhggH_KPBAbrR6!*{*R68ov+z1`GB6~7${z<2UTsbWh6(O044msZ1bR3a z7`mZq1sE6@SWfft%X6ZMLGSHj5anTGVEB>G&LAq!#=!6sRE|Fa5uiC`R>)+p7=!o( zHU@^*1?&tgEE0;K31A6>0(J&Q3BLk%1}2HL0(J&wi8_$9M1KK01FOWs0(J&Ai7f@} z4D1po3)mSrB<>ZkGk^|)0C!|SYfLzq85*JWNEW;vk%X*vVA%n3H795$iWfwP?qy)$ z3@>D7@bO|}V0^(Sv_TvaC7?{e7=Et;CL^$#je%h^R1#zz;|mskQ1yW(#=yY&s*s&Q zwTK;T+yST#h;f#cFd2bgYzz#Cp^`A;KvfT#7^HjvB}%RfP}Ly!F@{g6WM}wcz{>#$#1E>a2ATWj>sbpvPQVEigDr0A02ykOzVBRQ| z#m>Oc0AVI`FfeQYGr{SK3!3xUKmi6#S14kTbOlb9!ceuK3XypuSUa*1ELoz6fwi*~ z=rS^}fL1nfzA9#C*l>%3fqf1myL|~eLv|A*1A8|kBj?9*c7~nDSQywLB{qwX8ao4+ z;5=Ex&cNfx$iQ-wk#l+q149HzCXEqd-eH))T1L*kMn(p(wiS#FoZpJr8I(Zk^XD0_l6BMZZPjv@xq%$WjnQUD{T>tsfT zs>zHDEXNobSU~FqAwFO#XJ@b|XJ_DC2a0mhC{6|j(CRM9BnJitc1DJyOrRFl9#%vP zOR1WjVdYLHhTLjUaR*L13?DpM7?{?AS~8P**%_D=eHa*+K;g}_y_%hY`A9W8LoCSo zs!W{B0mqoYdcIV%Gq60bW@iu(V7*k$&Hzf~0t~F6r499>73kMn=x?8g>Q-&IAw#?nuzJUX0;i zYS|gA>OgS|mjIo=&lv7g$Ij3Rk_ck}U78HCo*^CN=|f6^(nsq1Ftf~S72@62;IlQCx#}*S_`^32yQRvoF>NbM|JEBiuLRa zETTOqx_8gpK90{cIk34us-2u33hl$|>h*QJO$iTn^ zaxOUbK}i-IAmE}W+`g8bp}m%!foU#iZ5*hy0WF*XD*(kQWB7tvc7_YJpu&TJL1+(n z?SU{TQbi_%0#blM2$Cj6Z-Cm<>|&sirL{~944j}QD%cqD9ZawpI*9{}3=B>+>T*%{1b@Ct#Yo`Hb@w78BH;&!m=nW(ClAXIN>U;wX|VFSf9+YJr|26m7E zj?o+pjEo$UK#87XE-wQEX#8hCBLf5H>#0l(4jfz$85kJ2HKjpmfQOm)7$XA%3n+pa z82ET^GcquULO2YZAQ=Y^ep6`%$iy}$NY;Ua7Zmd#K@J9loWRRIb_Ugcb_Om!Mn(qC zJ>c;IE^kJJO-x{$@*svoY^sNFAT}v*@W-LpqyVuAB*?*lkYnJ~69kPPajifyoB?b& zXkjV`0|O5u*dKS1O&8$cS3)se0Ae~wkON6ha7H^j!@7PZ2CjXKf>+zw8GeD-ml+v2 zS0r*UFfnq;F)=W3gMyinfd^tM_;Llft-;XZ2poD$jQk*_;7h;3p~u7s4n2?{Ba$2g z=Sg9(pK3s+L!$;_`h0LUghtH<2nP~i0v!BoGRRQ_^%6*sg8?BY@U4rT!K9m=fol#E zqk!84c82B&>AbsWQ{R}jM?HvNWhAT|kb@QcbJ+XM|tkRS&GLXLrf4RlZl z=W0+EX5?3a2mjn=&^keWP{s#)p7ZxcP=U-3%G(fuoKCQc%P0kl?wv=Gc^!DK`xurM$z zhf0EqQ}!1uqL=wF#6Sg#m=Gwv;j*$Pn~{MHR3!_fOk!tvH;J8rU62t}>@u(&Wq}qm zy0ck8#mpH{H;Cg57pR!wT>l-kMv%*i6*-bYD$+nr0&pGysqBXE7&t*v5|Bs*r&>gc z=D)|o!0^EZRBC~AKox-mIj|`b*ww|(P|w25z~2Q*Y}dO%<<-6P1ra1Eq~fp?xf zCxj`$zY)MQ`~0o8Qi_}Qb$z@QH0G4USKWMHs@@EABj3ZWST>@*Gr zPLOI;Au*5^0Z@yAjS31o59ZDHWO@;)L|r(Kx2F$lccs`6J&-r^%xl37#J9sqBuCDWw;p_8oXE-Sf?_H zO3eX{b%9+BYCbS9u(b2&N&Z9^T*U(#wE^9*4OI#re`E{~n#s;kuz>;8;NHfm{go zpC&g0!v>iDZlikzd@QeOrL>1(^0%~b8FtDv{WMJSdUJ5D@c|o%fa$KM?Sm0h0 zJA+g+I|J(kCPo3tR(6K^jSOH817`+AF(`Y-%7PU0f{H~sRt5$}4&H5z3=H#R7#J8i z85lSp3WDy(L1aw>MBX()R}m?^-k@6W<83FI0D2F_#PRu7I6n^Pd3 z15_}~(_~4Ovd1}+9zC9{W%fk6n=bOlBA9Sa5qf%56>4D+Y6GYEbJX#{sgRCrN3 zE|$Eo{u3K$!v-5@0|F;x(*f_LtIQ0brXU9cufQ}WhO3}Xm;f(m(?K~OC^qHly4V?_ zxi}ek?HC#5p`{1IZg8GYMn+EN2NOXlgMr~Vs6)!eD9pgX37Uw4*2YsojYkIF za83q>r648;gTRYc$ea`71x7~Y1$q$wfU01|a8M(Wfz73n9byKwoekE=z#!l|1FG=} zsF=9V3)-#1rN+;|zzuF~fcilqo$L$|{GiMW>V2tqvNM!Hc}%=|o$L%-Av^|7kU|X( zUYSmINXp~jl?N#XWp)7uPLM`a*(Xx$3}s+hUKK6|hBi>6hlLl^Y6go+AhatmaDr^o zfHWs+oW?)QU6ui~L&Y<1P&cK+>B zS2GIg_pvj8EMlAtZ2_}^bPGy#L4&}Kk%2*+K^qcyAd)fMegQi}(*kw|#yJAc)7Tk6 zQN_s2$S5oTRkaD6J@f9dP3E7K_^6)b-=t9#ns1Oix z6<}ZhTU7wkc9>BfY}J7vgjFAckgZY(W?^7F%*YS6ssO}!!pNcuvg$xE3j@dsK^_K% zTBx<4#)OcI0NC28P%&2q1_rRT3=C|ho$L&ppd6yW0d7jn0VQ1qa4i-H$3mTJ1_Ka>lTl9f2R{Qt0f_S$RGfoKI)Rt{Q1e3>83krffO5hZ85tB9 zv>;*n7o1Sb=R+-PW0dX?Wnc*KXJO#&Vib`M;AUW00OI5^YDqhDGca86XJKGj$i*RT z25J@turRPJ;pUKvgUxuEqIC?^BQZ>lH zzyPY9z#ar$7{M5xEXu$DPDlSkj5Qpp&ddx93m|L;gaM##0b@8QU5bLzBUC?Vc?1LRV-5!O@;*p$%zK4VeLb|; z<$c7c{$m29IOe^@$RJ=lnVq3_GCKorJR>8g=yXs^k!ztSa`%-Nw0~0+R`r1Pi{60n z7&t*v(E1QGY|6pGzzLFv3iUu*wt|Y&*%>mYvor7(g9b`L`!&JCsF3}d9N_($9N_($ z9FYB*oRIyRyr2d)wDk|#uLU+u6?j4WH6MUhn+RwGK$`)eiC1_Y+^ zfD-_+7%Txe_#qNN1BA`MAPS0dE@eo12Q9x4n8w2(7%`EZVHwEfTNnkoCb2VwPGV;e zc+05f096jGf0lTa~xkk|!?x;RycIwz3WC5XBzHHersNbJ5Y z*p3}gF|ZvEt-xZj>JWA6AaxIPL}JAm7?d<1Vws?+r{@qmvY}#Nw|wLQ>)izv1Dp4m z11uJ#3DIi|GVcedT?iQfQCt9(0QFQE!vp5AGkgQJ%|Yvzz|DU3<~E2BXFemOImp1k z0AetPgSrU}kcK&Eb{m}XB{;b5Ng!t)(6(qMNm%9q?T8kI@EABjQqW|kdA$?=#mGO&SmQgcEk*Fjr-KqEr% z3N8kmPWQ}X2XCL|@c}mmKKQXP@c0R+*h?}nDEPB5@c7Gs(<-b80p$rEH_$~1Y@jXK zY8#-T1RCC84A-2`&X6~soq;Dpl!KLl;V@JZREF^+fs#HnD8Lo;B?*w{+yr6ZXknSokXk!Ss^oJ ztdMgi*g(rkIU&nQc|prb?IB|spyj0CkzEB|&~j2R(|~~!vYeDFOBy*Xg4T@ofx7MB zaY)c|(S;Bm11CrdIvunNB*wwO36h5j-Gc}*uz{A7f*m0NsieWyG4O(xlY*TO+FuP? zP6}2dfUJgrf%8~5Xn>ygKsP&sE@<=yG)le(JYI?`C_u_Udn{NXrL;xvM?|RH?l(_pP}3##Lp8FV01pt_(*g}X8g49q{_MuIK; z4Ym-LB$(M`;TH1CGBAXIdWDdsw%~w(3~!*M3DC$4=oAmIX`ZquArOKv4KyAQ19Gkh z7Xx_bHY;@JcAz8!F8%otNyB^v{HFC!;pFC$o46sEGzTnr50y^J|>4B)+tV$i*e zHF6B#y^MOGaYTL45S%`!dBMPPSCRp|Kb^s8D`;4GK4>WzxW15KxU`U+Vg6Yr2IWPF z@#2KzD6`TLO`!UKfq^*)F>?%>FlGS_&$Biwu`)0+vQ5fiX8`XXU|?WR6lGv=jRMah zKMdnwVBk394I0-x@6Eu##Bsx$fq|K07AUPj7s>b@VP}|rgq=aSnTL@RHq$Tc!pF!7 zo9P#>;b!E#v5%2Kc|RkAFgG6~r}2J9hAI%pkDHOxN`eKvIsmMvS%L+;IsmLkdJQW= zPs$orgr2%f>DZJP(>g?Mg}a3I5RJ^GpxMK&cOSKk&(0c7CXbC zTkH(HvW$!jti@MA4NI;p&}1;gO*{->NA*FbouOf~8Ny>g4jbdGpcNv#APVd#o2?9x z**mbKpkV_Nf{fS{ac;iK&hYgrI|J`kCPvPs*Vq}JUxRXHTxVyv zbsfrSyur?J_y&|S;U+u7rJGRB@7wGQ7I)Ygcv+bkIdku_GpxT0l_|f+&am?ylym$( zI|JtfC`b1pJ460MDCg!wb_VH3P|njw>2|9-hH0>a?0F>=OO(75tfA*K1LF^wp1Jg#t zT0mi&Wsos-wmFQP6PH28)Y-se>MWqi1Mu=V;j|SHMO@Pug{6+OGh`oUXW*I!T3`d3 zBw&S11h9devKv&GFz|xH6O>^XSX)4cm~epQeuB#bUXW+Oa-h9anhXpqxAmY46InhA zGBAj2fI1p=4c(c1F%mtJooW_OUZ^ zcCBV-__LZFqNi{zJH+VE?2MdKH$jzEvomsPZ)RthyBVtN@MfrK;p~i@r43Br6O5S8 zv4PH$JCw)B;K9LWoypDsDrPwt*g#W3U?vA!TplBXJ39jdhXC7>JVu85piZ^}+fEqM z5gcGWsT>TPnTyyN4u~@`Fvqbla&B45&LAem#K0WD!pOkVo59GyIz5k(!GME}Gn0{F zE;t_8(()J~HVUxi!k7|lm3fQ|pjcqhU~7PhDzJ6sF*2M6S*`<3>A{jr45uWS7+5qJ z899sBu`^s+$IifVmWh$`K8$mik&)A46FbAab1V!jt3fMBx3Du*Ze?d+`Oe75IcY1D zW5mSBxqTZugZXxL1{P)}M$RMK*%^FyursiPFfnq5?POv5$IS55oSgPmLuAn48rc)*%?|vX4Wt=ux2K5FgS3qT{_PK z@iGJ3Z5We-?InyU!1m!h3j??skYEF?MgYfx0wO{**nYs&8nFF`F)cVbKd>`+USMHh z0R_IxRThT+t1JvGpd(J9!S!`1JA*+UBLnjpPDXx)U*MJuLl$_TbH#sX#GPdp1ua5o z2xDPjKF2H|I*pxyVFQSh!onwdk(+_xLl_GKiw>iN*b#09hJbJu23BE44hC_M)D)7;uu{?2po#5p76ukKMpdzU+zbp4K&C9@;$RTB6gpMiXR%4wHh-^+Q&j&LRai7ex?eE=&* zox$!g69e;24n|J?*X#_wuh|)xkMb~b9(>Qv@b5j;=-pq~8C1TqGcZenvd0euhk?^% z1v^9aWflfzP;TS7!opy6g@u9n2O}e=+Z7guY7plQBO~YhjqD5;H?lJ@KVxKM1)XdL zZPG9cfT|t_*8Pc~NoclJ4D1Y3!IRLO6~7=;jLcJ+K;=JZ_8eSCa4uiO&Y+gX$iQmO z&B&>-n4O^n#NlUTza3NM6Uzz`B5y zku!V?JHyg44hGg7CPq%nZR`w&p^OZy2YDDddk(TQygSIwzzQS0gW@lC2G%(|jGP9)*cq}xoS7hlS04wpN5KZ)Mi>mP9U%rMLJS5K(V&Ww zbK7xth9Ae-89)Yu_H`F9GO&P_vOxmgc_}*sa}FZ|`ym!aPToC?3=1PU7}yI~7&)0v zvV&_Ob{Q5%&KED(;W7*ioXd`|GZ@q{GO&MRW@O>DVqxGscZ8i`B1j~hiILOgC_BUT zLPiGmY|wJb?=Y!{%#55eN7)(5KvG*ksSc)YKB!Ozr4yF2<*bkrQTXvPb_Vvnj0~KY z8HK%HvNJq*$qr_>?Pp}Ty`PbRGmeqd9aOAdV`1QwVq_HdxX!{b`#K8)r#mQZf!hVw z@)#Ld{#G$FaNb$Z&Y-Y?oq^Afoslzb13SY~5a&NTBTMK4HU^eCzd0E=6*jUnG;d^Q z;A7)p!-WNE&E2!? z3@-bb82DT{85tNjb3P*J-|!Jh|IJVA46>i0Znpfw&QS4%oq^AUgOOztsO1B8bLUs6 zVb&asoEkq790mr?^zTUe^?o4qbH4qBWT)Y8gq;iwEKjSL8CX75F*6AJpJHcNdWxMv zbRMXfX0e|JZD|ST?qg?YFJWa6*}}-OSCNH*Gi*OQ!}k5`3?gD|j0~KUjRc{&k9-q3<+1gUEG8;SV3!8LnMpVGz-06wdg}&hYv(JA=p`M&Z@p;cN!h+5@0Y zAh^_M;|3KZY-I=78Qy^w1q$$jL|$xWW#F7~fSuv?0d@uv5jI8!);o}rM&d23q~Qe> zI3R@#ENQqkFtC6YjHs zh)trPYS)ZER^kiMIpfqM=UBWLA%c80C* zVfI3oU}p+4FmQw0J)oW@$R9nR{kaLuj=C669b&$Z;y{ zWoLMCorQsWB_ku}hjU0A%S%X{$k*%)Q(wazbag*FXvO(2kb^)S1x5xQCUB6;3Ne5d z^+SSO55j?j0}~@}gb+0Dz(LN$2o7?PAR_}pj`Q1ob_V+c>{B67qK77( zZx9Z|a1IWnLmL&c#xf;5yV--%*a`H0!e23 z33i6>C)gRdPl3EU;V@|54_B}-!n=@wEd#BPfW>q>gadI9BO@Bs&B5OhyJ)=;np8DUCGYCQ^Y7dZ6!Oy zLlE1Bk@NT-b_UVC>*VlNQvS1;jg(77?X%uu_2Ut?kLe9y%oeh5_VGeGyTfyc2y^Pq^$LdB;6jmH8(jdMJXr`Kh zg+UfFKreX>bUXkfSSe^15Nv-aXl{iCG`Ar(_re~4dNmq69N@0jS4PlYE6}=i1_pRn3t14+)l!H= z>S_fHD9%6@ggApi zV5cAh!+y{fLESjBg`ZN zHsuC3Q&0pErW8Ol3W()^)@p$`B1}SHBQJnBl1vI@zA$zXQ9&^Vh6WIaokLiDvj_vj zg;*8_jtoPEtDD3=MugZ2$Ga)^R_ngHU!a#cK% zQE(q33&MSfEC}}@r2N|u&%(g5O$J>4J%~pt{{#|P7(iYTp9>nFf?5R{wP$|9E(ac; zazqyV0v(@17KDr=flII=&_)af1_mWio`e;o)lfl{5)4@oT7oe!fXhsW1QrI4t!m&h zvjF5eSeYpaHAe?zIIPTs34)eaz{^ZSs7e$Ykp-bPg3C;lvo;2MJ=5fWlzpz_85#DSDE@}T0QAd!WEqeUHDdK>`Bfl3chrkMr}MNnPD z%*w1V<4pi~WbH1o;e0p*;zYzz#+P_KX%KQqJfBTNt+7qI+@EC|VuprxPNK@FT_76y)9DbZDI z3=9iEhJ&miF-1ZY zG@?1Ati7sQVIM4bXM9C zXbG&4#=-!qW59+7q_Hr7q8)5_LmHCd8$dFUDF9G-gIx6>jfH_@D#TR+=`0M)FjqOG zBWWy1N4N^4Ye6~-1IILot_#Sz7&4G_8Dya9O2}Yg;Fu24H6a7Zt^>%rKA`DR$Yf#Q zm;uohkcp(LArr~24VkERJpk#N3DG5xg`~?N3rSZ&7OJiVSu6}3vmm-IAnRhtM$%=F zjjAgln}va6HbmEiY$Ur5AnW>oqzlw?RLEgr;Fzxm&R_vKNOfETh{MPMX=J;zLsB{@ zH8X>fBdD$K4HX2ZJXl*FSrAfTh|XnWVAudM4VGjRkTt@REV3Xx$trH;Vqi#tDhHL< z91Bdu-FQ&Nn9p*Ffh%2Pg{&n^0^ms@WI?F8iamS`3RdMq7l*%cIAMi z8gTgtuGU1u_!t;293%^z zB;A91NcW+IEDv&B3KIl3L?A5}#q(UCYuTVa1SO&^8sgX45n>?gnL%R!r_cmhjG08l z4m@Z^px2u znE1f$w17fLMgXVd4ImCI1IQqG0GI z$`h&<+_`}D3?G1;1IrNUP!%Yqp$LLa17|CNBIFDK;=nTmhy%+IZP<)N5kwfd0HP6+ zA+A6*A~FPn=s!@*6tOU{a5M3XX^1f}7!eakpcGm(fX1Kfp{9UFsz99{aPJK!2wFx2?@M_?RigBzkOd)CBV_!k5!(6( zH|q|t^DrHyU#6rygMIA?pw89=n1_p;x76y(IX$A)2g`e0NTBkBGa2PVOfM{Xk zuh0=pXGY<>-=ORuMg~^UdUFX5Hqf4Z$czgcXsI`N#1%9T1zPG2p8AC>fMsA{0PU|} z4DYjGU|{h(#Lgg`afqGa(jj&Rp%g~QQa53d!w~a@9x@8gI|gOnVPuf|0QJgK(7Lnm z2I$DC&~5o077Pppr7R3WcR1vaSTHag0I^d9y0HhICqRoVAM9E_)f?$o{GN=Kf5t36kfH<&{Y!fz9Py`XCJb-9~ zO5B(FiHYl(`{cj>yIG0on`<3o2O{gn9+#b3y0Q zfY@n*VhKD93=UP$wMPu#7F`h3T99s`9zg+6iw;=~)S_chn!>=q@BnHLs5d3FL_u)@ zF9X8|s2C^$g_g*Ox1x!$KVoJPC`K2IX5wIAP~64K!0-b_zl1pG3@~Id_65u=0&CF} zvgd>KPXtvoJP^Nw27H8;$%%tfCo5D8E>Hx)!4FQ+d{DK>DH>S}mZIg?F)%P3sA6FdS}Q3A+Tiw~iiJTKbWEP&2?hp+ zBqU>m*2oHg4ii8Y6ZYqpWl#i}Q3F*8O6@|MBn3cbA&Vi*oB~w|a+=U~QLvfFVh}Sy zwy%V$1V{W1h)QHJuu5=BQmAHO5IQWV2uewZp}Ii9B$Os707_BFVvxdA5mc6#@^1KWT6QS~;@Bx=gYgo|)!O4h$L4L6?1H%N6&O01pN!kny52{%hxYsa>Ffgz* zo@Hm?Y(EPv%%?FjGO&QQW^#I5g9>>uF>+d8N8+g6LgGl>L*nq>N8)55bBZ4z$y`9< zFt8NfU}xaey9t$#XJTYv`O?kAz`5);R45W8l>dmGfwS=uRLBb?RP~shfwT27LWr~C z2@)saDU{R5%*fgO0?Nr_X5`%T3d-?fVq^iGI?lQ1H9~}e-@ybD?kAZU7#PFFnK{5m zlM7igi(98NFoZy5^*}q@t(XK z^bDwWuuT$-9N;-QWI=HL5w{d(V90?g1^HVDq*Mn@koz>K-W1;^%)n3xRSPy&hLKw- zoEKG)dl6{!Ap?UbD6}RlU||rlW|jqqQWLUPSSX5@T&Vqaoj(%i8P$+<-A0`NDG77<>e>+q=*d|b%fmp-RCn1t|rE z60#stXdQv71se+sEo4DhXbH%2Gcfp?f|3*i1Naz-U}g=m2ZEq`%UKwNLYNK2uT(QI zu$n_ufDSztN?;Zh3t(bkC|Jb8Ahdvq51fP+KsajPba4U1fu#$E#YpMGU@;_JfI~z9 zYAQ;GP=N}9oeuIQC`6D25h0=hRSI$&JVPK0B87-9R4v$8Sco7C!b3z?l!3t?sv6RY zVG?8jr>ahe4kFfb&5 zqS~HWNx<5Ofg#omWC3JB5bt#+Q3e44ZU%-Ds3@pX7II*g1`Ad~1;JtGz-$7#uJ#!_ z184Lzc7|=w*cpUCbzS0fc7{FAAshx4?zij=oYHTRst1-@kWeRzkmfsf22QJYNUBt9 z|3bD63ps)|ZS4hZ%4Az_$^zR{k-C>1!W0lL*~`vwU@tp^P#mMMz&>_{pndENLJf=z ztln3^Db3*XaU5aF*p zsG*=p0R^@I$Y02U5PyN23J4g$O=c7o2hG|c3xbD%K(lvSp(;Tqb}++c@eV=-L0vay*eu?4s35412b#qb2kk`p z1Qi4&Qs!9bOh1YsY$pn`Abb>7{2Lbo!+#_*gr+gdfrodI1({{J`M|Ro$b#_UU1UM1 z!Q#x^3=GTypzvp4Fa(7G$k>^DsDjL<%pBtL_)!JKoKaqOhj=5AF7zhN>Kr8LsT(7cTP|*2{Z}~THPg3%fcWugHZ-N zyyj4gG`wa9H5}Axgb%O51VJ8y53fZ)Rf3WyGi>GwSrD8&86d-Jo`R5|0c8-7u?!62 zpoy&f`|QJf}lDTJ|i{{suIOr$bv9; zfwo(2fhqF;tQbKh>?-=^?7y% zRS+kik&#pP0y{%Kh!erc$XR!Ronb$S13C%l>;-lP!Het+LRT3XIh8K5GsJ>8_Kb|2 zX&2cU7J)d)jEwvN&;SDU8X3dI7&#b%7&#b()-Z~nuw!6|hsuIHDzuhS7+h$;1i>Yw zDx)a4FwBCg1o<9Z5`YQ~WI?d+!OidmAWwo?=%B*lLM^=b6@6sKz`#()!XR{xQB?G* z9Rq^{h*QWY3NFAFL2UrL#hZ~E>=u|HC>-I{6ig7sEy#jkw}AUROQD8>OBzsF2XYg# zAW{Lg8mbml0)XrS7huSO-~tTXJ!t?rJb;lO>_!6-NE!rJ9YhIg3=9oWrC?_TGl~klM-zk;(jcWrL?IynHZ_!y1FRHT5UiAeLG+Iu1H%ST*sW(2 z6@72V!0-XYNnqpvr+_2S2#^MqbCHZZ;LwE$qND(rAlO1sw1QG2vLGUKk3$VbNdd@$ zNTGWcsum>$APa&+7o2nr>RA|sVi!w8g)VLn9` z1p8E!)1HCh0?3$CjFRAFqz}~x?%`i!Ns0;vC4WJ-F z7DP$}u28io4o4P*I2@Ev13(UkCDa8_eJBYPCWzujWI==*7en=fQv}F6AU7flBDrxb zR4v$8So9$ag53yCsGFclQ4;E7s32HBETJL`A`YdL5&3`q??RF;DiJd1Un0skW!&4QQ`wx5bRe-!QB9gke$$i z`v8aoE4T%rZbK=!VS*^-A50MJ2v9tLk`=NbBFaRdhN6^z$bv{wCIwZC;tga$us6U3 zH$xLr!L0+;2X;8D;D!mJI2baKZz{11LWr3nICZ6}fhY!HFE4A2^}6MS)F)WV3m~PSkVO`Rge@bu*MKf5ZuWJ_*AG$6vrV8A{;jZsuU$@A`3zs z2d>2yLe-)qO=LlEw1e~11(3sGjiJp@eJG6~m>^1?f(fEH99aRSHg!usnq<2+Jto*0}*FMD{@wWC4f+ zOOThL!3r)bVF?l@h!SQnL9pXMSpbw6J;qZ@8Ls1-#EQsXr?@+ZU4o4P*I2@E9KY$z# zOOSEU(hyt}!E!rH5XFtif(SPzLiK}V9uyCtq6k?K$&J}iwP0gm2@+Wl>_%{cEP*OT zDdd+z1;P4Z2@+Wlksvogm4clGOOVKd-~t#sKG`L@AJa&^ivtf?%bPj>7_ID1(Ro-b1HfV1l4Abda~e{lgVdl_)`oEQkog z)lj7%XTXCHSr91*H$c^*6qU$=;2;E#Bz^!zvk((xB+;Oig+T~5lK2v82+BwzOb{ip z!UR!#fh>sd#T%%h;K%`$3!qGoEQsWbPf)cei4|E8;tNn>EdV(jmRQ$HfWnA@0VT1* z1X0|GEQoO92B?0N#ELA4GYvLHAGf)lF?G_is`3oG!E1;I)g7{G&v3qWDV!lVo?S{0z~0T->X<_Sy? zCDLJnU^_rD2`b@{1rY(N3^f$&Nsv-dq$3L=1*j%eElQ*#3xWd_T(mv_IULqJv4-jc zEzyOK$-o3r91asiaX7Ld!r^vMLs1-#EQsWA7pPhkha(F@91cpY3T;U3T9F08N};JW02Fqg86l}P4jSetsTC%O66r8Ou**S75R_Vx z1rY(72sIQX(vby`0yG_}7A4Y=1;GIdPOTF_4u_@IN~k`t!(j~pm>`P7VS*?QM;1gl zyasA0io=lwksRIxRg23-L8+FHof@sZ|9zUBOZ-vLGV08bXz#q*i1>a1sQk)<00CU{}G) zR%Ai2QU=JNI72%NgU}a7Mev}w0f++|6i)zgK+}7mggBucc~JZSObS#?erSg^h{3}N zBGRCmhJgVk-jD^sVFE77m7q$&qtsuatz?)WOh3340uuy#36?pK1rhP53N;ku3V167 zSr94y)S+r&c7f|gWI=>mO`%G`ZvDon0G_jg3BvS)-3k*#aVxSQ!mSoiLs8s{EQsV* zE2vtSU0}B&3xeGWp6F2MU||sY2c75$0C8Y(+5qCf;&ej?a-2SZNrB>2pc5rdYoT_d zwAPRX!FGdJWo?2g1!wV}jF6>T`=Em0Jo%H+1nf$OP8J3sRwi+9NcKY0EO;RutP2bi zLMDYN! zAi@JLpf;g+09g>p1FxWJQ9OVw2=)Lt@fLs*FRaR8kO7&_z<^RM!UR#=h%AV3BQsP# zIM0AeH&7Nt7DRF*CsZxiSXh;VEC_ZZxXR&!Dh2ONfb~7ip@Lxjuqp>x5K-mWLzRNo z+z7$OZ;%DS^$57i@r5b{ha9ZRK^6onWnkcZcafbz_YymUkQO5&e*sk21@MW;ix@c= z9)N~&WSPKu4<-xljmR-U)``FbLDzkNdNbhk+yFHP>?ct8fod#dK}1kDLzSWgHL@U5 zQ1?L9!jc}i#6=ba2Q|2lx&V|MFKLAX5;~%q2kaDUFQc{g&77l1{4qQRhaQmK~T*IUxir>6$CY? zL8~ws1TG6QFi6Wmk{9UYDUg29%0f^YECAW41g3xd-i1B39Ii|h=KLCfyGGO{LKW@pggV2eD= z%y0^HdH@5PKj%wbB4hBmm4hEqNCUG%F28Md5 zEZ9f6Ond^MBZQF!h10k}R}HGg`Ghth>4L?_XaxyXt$M6EE6Lq!)u@+-)LJkP)|mjkr@3ACF4v>{Ar6%%+^k7Lk|lB$T#5Gvj&ii7n31)cjarDJ44us^{g|1+RU!EOcx3CO?5f=KCjAyh3& zIz|=*r(9MfC@&6lL~IR~h0~uzpxYg)E4usH~t$!HE}EQ6US0Yaeh$6|I6~Dy*VH z76dB=&7Lucg7QTHDERI(iHL#rH7)>gz)8N6nStQ~h!C$CSu$w_j zL8$;)5GfTfD?q{QywpWpy;cmN|mczC4&#DS$yF=+ZgaT~HA!fldJr6?&B zSrEx>Dp0jxV__*2SrF_ta0=ChDn(g191Rr&>xZRKWI;p&Gcarbh4eZmDbWl= z28Iv)&}0Q#m25D91++5^QgRo7IIxmi7itnXS-~b2V1g(mw<%O5*f*fG0!miMf{3WI zfGP#M8Ke}HtdIqfqRtVj7A09B3xcB#oF6uT91a_*4T0(d4^qJL156O56J z;qY*%p(qYV7DRG*EL1Iu!;u9c4hNOoA3zR=mE1niRDn`*!vs;>h%AV3qd!zXO1?lA zL~>&kR4v$8SiV3O1iKNOFOs23QA+M9P(iSMSiV3OMC6Nw$Vy@P0$C88*unW?y(*+6 z0hDq7-)Tdz(f`Xh#aWlte4Kf&;XMDzy#THy8*<3ZMl5_ z;XtBMTy;<`+~c$b0Zg z6?wB(fkOm4o4P5IQ%};P!xwF3nDrEDO4?r!;u9c4hI*KAcw;W$#6AD zdI!fctnmO7L~$dsAi|ANQ2pRB2w31E3nIBO5vmq!EG#D?3xeGUE+o^SN>NrTPKFAC z^}`BDWI;qBIR~l~>?~Lzi7W^%-N1$9YN%4Msjxy4SrDugT1ZYoE+nTQ7m`<)85kNs z()*aC1y&j|FepO{Ur_Y}%HEK@=3!7#aGYLYk^#?Z#GwkBfd%!|Au$71c@nD9997T^ zoKv4deE`mXAnQR5ZxlgrraTU6xqw`_o=IAu-;jZ!8QQ{vyAW*JL8xQErh$}#OhXX_ zna03>0O}Mj@bKy+CJqMBxhq1ynZSn>z+}O@#s4sIgN=p>f{Xn~CJt~9;Q`1=!Au-t zpc797rm`>~PC7XYwF1=DhaXCC9x4c$I))!gfGh|LWKjW6r=&H6cj4Rf=Hpl1yzeuDj^GkLj~NGxd3uFtW*ks>H~)l zEaqT>;EW23Ib=bE8-tF&NVGC7@If&s zf{{!d;B$hI1>v~|SrDFkV1g)dpboVNoE$*`3rZ)*f`~ZKhAKr#C&+?Gai9xT3pN%O z2gripH~@9vIQKqfXZZCLbm|2YBj?#?>^EVop7&$LLM;fK{c!A*XUw|5)$_*N& zyurl506Gs!h>aOsDm{eCf<4O43>l?_38G}i=TMbkXM!@L_&a6>hSyM0@F*oavyk{# zLk5PAP(e`91uFf)qm;;k;Km(jXC6!tWt8$OR6E!vSU4jKg2Nfyj$@dHw0ziL8d5tB zynvWb6BGdq4B%c2Co>%idvI)+IN zDhl#4ytLMU3Zj(O$b#_n1`|Xnmvo@o!8XCd0a*|n4&Y{<0aPi-XYg_fSr93`nL^cq zjfJH*WIoBNh&=~^o6?`y3@E$x^Nf-xJ2`+MAp@J-k2$e*rQm`{YN+=JF!-L1B)z-V`Q?;zndagd1a^`oT#Q=0;>eWH&<9f{lg6 zTso2<*p1-Qx&SJQ($QWB6$I;tmDb3Dh|+o;R4F*xVWl;)Ah-wym)3@ckPrZy3M;LV z1;I)|eQD4TX8k0#0e7w#~Q3DunVdZk+?uZoD)EX!G<_5Oot{e z&=9A<3>F5VcqYgYX8?!;n>9WSH3^*kVZ9ZYAWHr_4^;{F7$|B$A{H+}m4e+2 zQVL3G$bv|*cmt{yC8Z$?f@2Y!(k6f$4jbaU0OG)Mfvz4>po1&~xeZwm;Wh)PQj`>i zEQsVbYp7bVmtiRkSrF_ta0+vQDn&_QrBFezepm`a7DS}5CS;|s6oxDaPB!2a_7hnt zEQKKpf|Y`5NJ#Jf6EuXuiKUTQ0$gUH2!e_yaSj#+hHp@%;KmY2DZdc|!w;w+D271o z1P0Mv)eH;_Gg%mf@}Mc#OdAsZ;FR0UBnP$vCJ0Uiu#{^9RS9+s$X-!V76yg_kYUYC za-voi3=A7)LQ^jII0ujntXOn|Y6d4(*uW`F5M@lj52_Lz@~{MfEQm-Dfl#GjXM&W1 z5-YMGQi6zrss#lFXi*4w>K0iLoFKqs0tU0d{X@uc4!KZ$@}N~nuv`WcL>Uu+38FY0 zSrFmy0;r)V4o4P5a(Ee3EsDdD1tAUx^%M#~4u`eA4?+t*P`?o5Veq^-Oc2G5$btwr z9)apd$!*AjNNzk2RSQZ=AiKbUi!2CsBRIERhbl!G6JRj_>11F4hX*XTAqygMn>bV{ z*axuOhAarqLg3uih^;(F76dDWj0r3N1zijyWZ?9|EX0_As0`@L@!2d4LKB$;L?2c& zFgSoX`OK2wa|;_l9M~Z00SHG8d~V?f5C?W{p~4)bLDYab;NexsAgZtt#Oo*pwm4J} zoYg=H8&oJF3nHRP8mbgz3cSIDEQl0M3Q)CRV_}6NvLHB`K!d23q6`dnP}QKBJ@6nZ zgXm*k28ITZ>)V+`6;gN^7&gp-+>6K{dc2x};Q>g>l35bm-kJqXwP!KDSb zU`a(TgJ9J-vLIL~q+n?P1s$wl*)R{OU;$;O2OwEkW)heW%S@nSQXS@lmQ^t;f{U2~ z5C>MwEP!wz#mof|2Ug55ARn7+069<>Qp{L@Zk}ggU_dEmY@mYRj0Z~QpkfAD5D{q( zP^I9A04W9K2xLK|NOOg%1se;CG-N?=q=AYV(DA6*P}T5a2Hdw@2Q7R+R}=^pGmC(W z6qq2mNP+cjVS?Z&h7~DWpxVJs02L|Vv6LN9QSf3{*jUOws35504{9TU*Oej*!uz%` zL6lzHA*gn+O`w1Qg(0#aA`Fi~m4aOgQVI$~WI?1bJP%b1HWn6!$bzsi1P9DRsA_P) zlrckQwqSx_>tF%%9I6s*E;wNHSr`~zLq+>Qjhr%ONpVdh28NGN!46Qexy~di4jP$6 z76cD|fQpANP?acsUYH$q z!EOW>W%^L1;B*Qbp^ShEf};~wlpzZuin4U5Qn0gNMH#XnxO@T^Wo1yMV9&yeGGsxp zQc#bCU%&$7bO!KVO~&vNW)6nq%p43ty^urF6`-=9c~GG~X2?_lOc0y~U{eJXKxg^F zrV0)~Igmrqb)klUPiBE-P-Cbd%HR{SAUuP@1i{w9qSG9z9h~ApaRf?o$byLIw1z4L z`4irjLKZ}dP6w!3u(7ZViYy3@PS{|<2aq@WnR&pu)eou_oa$hM1u#Ky^uls$2vjB5 z5>Rdx1q~J$ECCM*K-NPQEP=L+K!XJvATp4_f)5}LY_K2|Y7$B}1}2Eo+Q@{e1g8jC zcp(cS!Yc==6eYZn1(Cul52_ZWwSg=M4li&PHCPH6mIAMbDgbd{amio_vI=d216dH^ zHfE?&lvW0^Ad=hoplZQhhQ%eaAlPl-tSJsviZWQ>0Tl%6hh=1SScuLLQWKKfrc=6`d|VxWcmO_5SF8m1>sd5vLHN1!34oc6&4E} zP>aB+5|%=c1rf2(162x&Hh7Lg7DS4LKB!u-v9MS`76iuvC`W;ovMd0li^ZYG5~R4&7*{6NWHKL5E)4Gs1h31C?!WNR1oYSm_LyP5&mq1Dn;=p zvLKQ_+n{Q}#=`uGEC}7E;K9 zh>$9WDg~EwAf=!x6Il=`q#B@V!N$Tu3Rw^qQs83s7P9JP%;MmYEEGX-3F^thz|a8l zJ4i{C1p~tqs6J3h0h;7tVBj=)&&~ik;a%t=Gb8^2Ye;ZI4wKPh;a~ur2QRc4Qtw@a z%7W{?Ezo)oCJ0VWu;AYSvJ_VDJ%Dl`_1-($5D{LoP^DlugOq~OFtQ+0cqv2GqSSlHg5dB1*LwjVhrdPxBXylLl#7G8y8e9*jQLxA`61u2Cnynp-NGD*A7rY zuzpy*hb)Mw_dJo6!sl&R{VjSB(NHJdv?KUq)Frp zt6`JK;H^tjq0U4}hsc89xCXg0avB4J!5S6@p<~Q~qRF7r62!U0EF{_h8sc9A&3B-k zXAeL!pnwFeZ`=rtVsO()mr)$Ndj}>6+QKEI$|wq+>=syy#G1_n{k&a(oLIk25)3)Uj-Jd?D8q#=-7Ks(34*FY*k1yOdMSwICrZD7#O zGw}KU?odHc!iJy!k0J;={~uWpdQ3lf>l&jY!~vjVhd>ShCs*+0suw_xzRe7|g3w?c zOd)>gWti7~K(AoYfL?$p zfP4Wa0|VsB%=aH5s>D1P8HAVpVrRJbi=9Di7Nc;%Z+3=tzu6hY)-tky#Dow3hN_&+ z$RK9`jl>sxpcN-sEF9pAIK>?0wYV7=7HnW)5OWh1;O1ap@PaA?HN8PM0gFm-FfcsW zz`_9LEa6~aP}s=Az!JtRBEE{BfuX?(5}ly>hozTARPi@xTpdYJ#DGJ<3rP%=4mqV6 zxy3p77#KRC3PFj1Q-)DoDNqzm&;xYNoal9428IHVwT&#IN+8n=pc=uZUEx-I&&$Bz z3>5<#c2g02b0CUXf)e;b4rDQC`75@OgMncI$bvE!0ddfMjdD=KKovYo1B-ssa=*@L~={5K)>mKr}*%xeXu=?Do{7*i1nYM40jb zq7hQe32cSjo(gvDO{giL`jQ!T&+$E|AgD72zvtKl8r5Kx;Gzt4*(@l09k#MCh`CFG zgSP-=_$M|VQBd$M0C5;Oc)-E>0mOO4&M!Wfje()V6%;&>5f0`j(3@va1i!G0fv>wq z7KFw$1B3h-J_ZJdZ7d989%7>VK^NU`V_|?_aK8Y=31yc7-+s%m9a8mxqagvr0aZPq z+iw?aXJG&ZzZj^Nxd7sT0}QN?VFyxty@WazCB9Gu!5YEUk^w{`B)$?r99VoYyFt8+ zVhV~N!juURjga^{0OG)^r4JwutXh)AW+aLr!bpXkEDZ3ttc7Ys#HDC59|J?cPUuDU zn)4VK7JxXAi|oO-NPpPL!XOqP2)^b%GdsAxK^B8wQ-v%DuWyhAp{0-i&-hTqm}Qwc#JBOI3i3H{a46nHQz>!?QhmTo1T~uYLfK`+4Fnh%SUn(#5af8i zFm@5ezkH~IpnIymX`zbo*>Hf845*X_T?sC*hlPQ|Qxse%ChUO}isGOPoW-DqgKPv< zSm2wS<)MNor4@=GxM%?zAnOBh9oPU^C8rD(1Z87TDJuXfWl;pd1_;D+GcagD)q>-U zj|ozEZUDIvR(SeDRiHQrMG$NnxCnj#(FiF#1@=NJJ8-G)0OG(3&thywq6i|4EP!Z) zwCNe4g&3mnRNO4Wzz_@#F;JtF!-q!+)Xqm1;N1!VRu<8 zh%hj;BO4JYF3zOQz|ad714TXeJSK5)({%z=5Y+7A2vPtyU6IA0O;>qPyff@$Vc-aX z#Jj^jq<4%HAo1(~;=n4&0uTol&+o7qi6V$FasfmmB%T93A;}sM&kCS8 zy|AB!fg?ah9@J_UIKaZd5hUJa?fGleO2l@t(L=+^@84f}M9UNu>APy|B=Rj>h z32YQWu+zXA8$cRifxQ8nMifD?Mo3^oG(rO##DN9&8EmGY2qH`oID{P7APzjRK^$0M zKgDJwiXg(s1rUvpz&7(j4{XKN+zbo~z6fECWOnffJg8#K)0tESE~5#u&f*1C^YWkO zGB8{?#KIsJB?+#Ev!Ql~gQovQ#K6@svKYJ?Mizuu!!SWmiw0f|BMZW-VPrwnYPbjL z0`OVWqJrRR7+DNnwatPm1QjOmstqOxDhgl~Ca8I_6RH@zxmHXT>@Q?7xWAAE;r@aN zf`*IW{z4Xn`wLkR)n7NEE&%&W65=mpF}S~;LlvU<3nqx{FBfhG1~G3)6mS^Yj0Sasv5Fo>mcfIX=NEmA}~71>v4V z7DRZ`Y#sxHE7S!(j0_B7X=;jFxe#KYp*6ADEa2uTOdUuJJ{*E92ydPu3qs>r(Se_V zAr@+r3CO0|EaJv!V(>2WhQlljpbPSODU{DyRz}4y=OW z_JfouC=NprL>S3%45`bU3DpQrTj1dj(E>gO27_aeF0*LxJO+ja5Qm*ZSixx?1H*-5 zEDU1vKpj*D1_su3Q<)ePIJhosW?A`3@Q))KyCmR+rcO|0jhK+*a2d!91P+b>RtAQ1tPBj&pzFhBZh#9)2C0h{3=BqB*cqf= z&t+f;0u8DgMuX;AmvJ*Na4>?bm0`HT%E6GM$P5lUkO&9|{AOWbYGGhtuw`Ii-~^SW zAW;Tp(51Q{VFnJCXa)v`L^cKnRv$HX2G&W_*%;VL*%%lkIM|I=GBGTY0fj4vY6l}j zR0ksij}tQ^M->w@L-7|T2A=thj2zyw91IIyGBNOQF*9-;OlM_a%3x*S`35?;4r~-moz6m@#p1 zJ(6Kym;mB9Gjnk5kY!-F@P>tf`8O*E*Hsw?27$MrvVntxYmYnwLjZ{LnURBQy*vZM zgtsgVoX;5f_)dZrUqP(~70;aAOtO5!VNekU1_n@@%^!&-$hMael-C$gG8%Yr5It5wf+(?i;3EqIj~$Z$Bu{-p z%2NrSAhC*KD5xO`@&QWj1qp%;1s7=>K>A<_-~)&QO8^F+M_>YAz*eH!G6{f>^ajUm z6nH^iiU$Y798d)n&BO<)pukcL3=E)Sqj=(&#P~oJ6-*E`d&aYXQH&2%VId2GDlQ&S z#f2=0UV(vAH(CV-5`&iE41B+lQX*)~lP8`@j&G?u%uEIbkRVG5lK}rrG(oX`P)g*= zh+tqy_{_q<6VD{bb0Hj|5!6Fc4iVwwK&q8Mi#DXp89DgwBME{Ulq~H$di-@rX&o#G zZcy@F12xg0=7RDC$N>J6Xo7sFnK>93xIqq>@R@}{IZm1ceJE}O*$lFR6gM(3@GWtJ zI+1~a0aT#!q%*My{73Q;NRVTrAh#O>!-3B%3>+H;r4<+$K7g5Ai$D#KFDwk47npc3 zYmB2zN_>e(^)j%5sA?!m$E7?k7|G4U&dMg>4p z44?)V0|U=uCJsarod#}Ge)8a8(DvkD;JLxfXMm)>2~@4$Wai*{2WrH8VPRl4WP&te z8bGdJ$H;+F;exBtCMJF^P|IcmNJTf3EJ{TS>Qhjuq7^|(%AhpP+07J%8J6g^FIXjd z?F$klx%NebVK+Do%{@67%0OZGkD1RFDGbX&Vfde!gX_0E1H%JQ7)CuT)m(${Kmq-ClSVMEJ%2q}p25@!W08t_c?sjbW z2C2@$!TbQkfz{Ch-&q)#VZj{m9TLn~bL?;pd%mMcIT0Mppmq~Rgw(NXGoW;E;H~Ka zYvh4CP8jVmkRZ4{21*hCJ((HizvE!w>0o694M2dC%0qBj)Zxj&a0pZu>9T@T#f0xH z3_J#`kk04@V+5uO{u5|9lE;Wu2%|yHW5lY5y+O{`gO+(g`uQ8s1i>vP{z#zy^R8 z>@kM-ctHlKcvi5%Wk3TVjNxx#GV9quegf+OUCYB5?(faP@O=Rr1J5=#zBfpD4-_^$ z+u6kU<{=4!#y6Pl*~L-f1JveYE@xL~;FAEY)&-jlX)H6_vvcrSpb08=F>>%Ax1d2+ zzbMX=L}@^S#4sArAVKs7G`QM9Ye0j(Z4 z3Nf-WND!8lksEMNz!`Y8HwOc+4+jI!E;hcONFjCu)R@@K#=#ekBnS>hA9fDRu>Hxz zfjw*)`2K+|m4iAPbi)L*4?73H7?P==fqTV=OdMQOLKqkVey}hoK4s$I`-9Zv2NjYm zTQ~%HwUE*{DEj2Ca5M0GgN7@i7J$pGtsFcIDCIFoDacTi3KAs93@VQy6&=XlmrRiH zwF5tpDmsRrNaJe`AP%geEBHAAi!_XY1t&PbT*8NgVct0w1|B_jzE_|zLug6{!5|FE@#i@BuDT%9gF1ga=Q%j|ry#_@$NtIQXB3279|o2<11>kX ze4(a1;@~$=D%1Nn3ps0@TY({U?U*=<(YGs zxVbnK7#IRTDsq{4P)0Ao&Eg0qF{}g1;C}6dUn~rq?o0xh-4fFJwfrwYbq3U7pqV{R z4<-Qy2Cm!g3=9WAPFukwCY%<=!0_Q03j;?96DTDk`egOs;8+9;j+Y!<&-EA>6n?WX z@Vw&S;CqA=!l0hKuoa^ck1AT#B70UBqYhHM&NN)>AYSAY2I|Voo&{wTk%T}{ubqJb z)b(QwSN4OZFl|n>M!^n0XmcTi6W%BQO$RZC^ZRo!e6wI+;7Q=*>qbg$peW->;}pOw zkpx+J82A`Lx}gaGH1ffd#%UrT4_ZNpB*?Lm?<-P50jXo&%_S}%fT0dEcdq18Lutx^ zN*CspTxuvyIglW@DTmQOWM0LkhSF*SDaCBHfdYehHJ1#gFV}K`(lt^h2CWES4A1fB zU^uv$iGin-lP?sh=m3>WJY}35{Gv$1X`m4x?r0_<1_pR^bb-@8cOW!6oDl6gf3Qpz zOeUI0Vi&^DU6I9&9WQ}yS^|nFyCZi=z zhJpUJ8u}HGKvK*1204AZCGdgJTO) zPlUQ~FmRsaU=Va*;#evKDIf$nm=Og8s1L~)zRv|RR3KQw#8-#ZgS2O0U=S>0lI8OS zZI%U_%fJ8<6w+hj!06Tsaxn{`%!`rUtw)*i1hvKmd6+>xRt5&v>5Nb>a#RR&Fx(gB zU=Vg;M0g~G3A6%5(UpVYoCrIE@Bv1CF;Eo_bu(xRNcbQlH~%lRMG4pi4>Jmbr{fQt zW?>LM$p~ExaRzzf_6$-7cms$78|h^@i!{=ka2C=576vVb*Z|_7FMjrdm9qa(~ zX&8_o$ndks* z=m2E{%9s71^pQcuA;#1ZNDw1&gF=!MebE(25IWw7(q#Y*w}FRhNLqA-l86);7#M_) zGYX(2B2dagOGFSM@Z|M{^YF=Qh6^AjxRY;ifr^vYh+g{}aCy2IR-RtKQl4JKR-S?c zF-qRcjKY{D?`3Eqn{a`JLHH)Lklk6t zdLav{Izg!mWhNKY@T1ZMI2~7-QMHf-H4;GO9?DuNkRZldDX<{=S}Cv~`dTTFAh_qv zz<|0|3RIngEW@bdaTl_e25TYPaEXOM_$suJJ#dMI0klvEva5+i)2vPi(l1lqVj5IIuh+a0RL1;cx|#Cr}DmP%j>o4Mt2M3u?6rUq>rsL472& zLKY$fE@TU?fJ#>I3ZVsHCb*Ej018fOtq?+~2SBULgl{vlpwt7PK02Bcp+d-w38)Zq zV*(ErjTQGte`JmPKpspjhIdmMnxTVOAgW)P@oPid*QGE9h60erFm@5H$KebN3qYJp?6O=&5ey6$uCXvku`r9sOM&L@ zuCp-6%mV2J2Z?Y;2m^z`bruGuFh*`ZC8Qn=s9nO8$jHMliYCZg3%Y?GWzC}}=s+ne zCQ&}*8Cy_l1Qlf{eLIjK#*{5c5PixPG9!VyXdfg7Z9*~d2Y{ju><0!06OjGZOf15T zAq)%&*I5`utw9q9te1W=LAtUWdt^Bn^yD}g6z?-Ja`@6{YM&5pK}D@Pz9u46>I{r-MPmw4mu=KG1Y9 zNCOCi8nvJqTr#JHL3Xl$r-hN{_&`l0j5$7#AjTXYSP*@V4=f0o;{#72U%1Y~pa`2n z7Px^_kp+M_u!^h!#DO*LHr#+zWJ6%B6sWg=(b)hAVsth@QIFBt00}~K3ii&%fXxeo zk}pQf4I~I_x$z+PgvA*c7-TPVpmc>nV$dbH4{pFKSb>`$Cb)uixJkuc3@eus2Lp$= z5(h)E5(k59Gb1BMk~#;&QFRUm*&aql)gfH=ZTG9;Ul0MRHQTpP&1Fag97VG;rxc>u(bWKtm6$PW;W zBK#mDLB0WDP|e5<-U7+MWoyd7pm3LkLAH=l06ffB0OE8)d!PsIBDJ;!?m-GElwKF8 zSOaGV6j7T37#JozU}2DLXSC*88o+0L_g+#kYe5`xu4zTF?Zg{Mk5oT+jsrIruEl z#FW$-Ie0vfoIjg^fkA1h3||+T7gIDBGD+}(G(tVV09K3J0Df?h4pj_N%WcTS!#^F#ZJ==-Zeu2P z6sthpG;R|n8Op3OVG_W!%8ZGd3*@VUhb#=-=1dBtSOKn}7CE1CR!C8YTiykQycqAPzYV zlLClFNW)|Shy!bwTmW&%X_zoPWnti!WI|1lprRMjFyUeiXJ9aR%EBNC8WGqM&cM(B z;#6}8^JgMO7bwdir7J!?q&x~roS;S`N;wG<`q4TeA0!dEDW-{pbcArXGjfOhi6C)+k$79L!F?y9$&*2#hIWPAlcoF zf+)o~ND!kq2MJ;n=O967q6eqM1e}; z7?^KzKu&&G0OG(Jf*(K}STow;HKZXpxW>o983uh45+n%qF3Mg8P%wa7a>(5XkRZ4l z!N7pBND@?~D_&xhKv^US5(H1xpiIet$^*pW7R(F$yqX zu9N=7D9FHZ-jai1O$`%+>|I7iF>Ny@h6!d&46s)q#Dmu`TELK$}1_n0J@x2@z z?5$Po4D&!s3K`fzH_{1vFd#3?VqlvHS#SeVmIzwr15u^|Q3hHAv_B108?!Fj2ptF% zd&$Ycu;CU51KTu4j(z+b4B1VL3~W0Y8O2njIT)-(!4hJ%#vBY$kC;J<7#LWEL>U;^ znv1|odq6fp90Xq;#=ur84VTLRjeawTf(F6CR26atLlp^h93t&SWF92>=IxHuSoU14D0*vKfhj+=ww^mQ;BG^+vD09vxj7!DeH z!8O=oa1u1a0-nuC05idZEfY>sZ?Hw|9ybTWj31~D11*nb3T zc{u=fAlR)Rz)Y}P6;4sltqfwXggF?bL_mSd$XcKaT}vnSOav0?VsjWdJVZGdW{Gkz zh;=hEiv3iABomN?pE4wwfF-sVK#~SXg42+LVaFq8unq<|_j2v+? z91KB0tPC8}7#TSV%sCi@n;025beI`ABCI(W61kZm%1&5wFc|PKfn~&w+j1}@sWF3N zJ<5)QK~EjbW?A zSuDuFz@)ND5Ofqa+gD8n1`7^${mE>QwUZ3&pm~~u9t@!KpV%&FGB6l$uum3bU=WL9 zU|>;Tzg*4EP!GzP8tldK>fV2pJiwE$I zJgX%P;Nzf}5;+(_1MT392HFF|82;9tgWZKpsF37hXpWhW1)^P&mR5)B-i%7{l3|IT-%LvN157 z<$VZB5g^MzH3G=d=Qt3@Z#jVjF3y>QL9LCEf$1VI^28Eo>j={&B-IDOs<$|EFt{YM zF)*FvekaJl5O9ixf$0J&76bl272`A(nsSO|wY)R9DQ%J)~0;iGpxq&#a)wNa3EDQ{`uRSZwEdNJ+*%1vCkI8dL=H@j!FTAw~u^fp`v3**s5^fx!!;nSmYT zjhP_31=wA*7#OyJL=`xuh;T4m7U5uE3T9+vV7sFQ4v~}3I2rDO@m{%>=6^c6Ku{eDDWX6AY|(Kuf|OSTHbX ztgvEWU}9o_X2Af}%MP+C&!2&Ti2>1Q(%=Nwdg#i!{J||lkfq?Zm4+NUxJ`y;+A@%} z45Hfst#32DWJst3mdGT4%`ivHf5L)uCXwfJ|qEEI0>S!gd$Z`~>L(8Oecc zGRF)V4u<7HtPBboOpF}+WH=b!fH=#U7&)HHa4;AKvoa`Tfaa}1@y0eA(!2yMpa9oc z(B>+~v<60odku^Xyf1keIsAH<7-se`G4Q(ZF>aA2vGEcMMA<`j zMh=CGED&Y?*%>+JX>l;B z-l(d2oC(yFXWb7<_$v}Q7?>Dk&Id9u$b#Baj0|!N?8g`x7(RpI1KfnU&B(yO5(G-Q z3=C`_DJDjw_8mx;i4nZ02CN@Rj)6^25ERuixqz)fx4*({)@_8CyMsd9!3 zlwE&Fb14;} ziE=PxiE)5U6HAxmVAv$d!N3y1$Z<)IgF#K6gMnog2O~$TJO@MCGA0HVPi{sIK?M$m zDLIS`EWzB298+MN#oUY>GKw4wrOTNZSQcP*FQZtd z0SCi#JtnX<(S{rhiwvQv));ay=ssZvJBFjnj)TE4l#zkuAP*x0+rv2U?j6vW!9?(8 z8}>3|PRM8h2Y9r=6x7*3Y~$jv;^JWN+r+@YvV@0`gI9xt!AFCGfn_tutZCA4v(_Ta z0(a&iX1PJk0;MWYTIEk;%uwU{J@%!2gYzk(Jkqg@I!t9|yxkkVrVFyxIlhJ!EEN?a79v z3f?`83=1PU82AfVz-5oZeg+1PHbD-Csuo5Df$yx09K5m|4Bf%35YBXS4u(c`W(ENZ zHbxF9YYv95TufjY2G;ZDu=Q8!!W;}dpO_egbV21A>na9zXzg(=kCB1(Zxu5GN2wSG z!%i^{21!45Mh-4<4hAoA4hG57?2H`uB{&%5BtaZ5M%HiFnHX48G#MB;7D#b0FiLYU zNL~V!^&A4)91J1aQ2leXIT)DKI2a@gIT%@$wKy19=ltem;MlLr!O*;sok6k+atbc% zdL3rS3NbM^2@Zxi5*!TD`k<`vw2GO5^-~oygP4FS2Sb`F2ZPc)MvnEW91M(V91Kd5 zjEt;Ga8ot3Af_rQfz}*?8hrNCV1Zd7&cX0QoP$A1gPBoGSqtYU&&e zQg&>Nte_eR;=*!Gh*?t3Y>XTtQ0Ia;3=AB1v^f}(bs$<98g*>YB>-)!YL7})nRGRR#9M<}Nd0|V0rN_)516ujUzzRD5h1FV~gF(zu9>LbQTR0gQ*qL;s{23T@ zeIr3LXAF9v16w%QbsXVhJPa(LQ4>iv4v1@NBT*dP0k@gulph0wQw=*r*%DM`>k-O8 zEkiRisNYVbD!YbI20Cw9Pamr66RNVm2xXv+-^=tF7+6mGGJp?|*3zuuU|?s_0!1DN zix%X-X>fpoPL*aci-MdgtplpvA--m2(7GnXz`(@Fwh=s%!d}D1zyL1K7}(otIUr4O z4h9xbfwokWfq`ueLK$Ry6ImJCKZNXBP=g=Uki{Mh47WjsJO?)pz;<21R5sakEd~aOaM1bMj4a=x7#K3N7#Q?H-6sQ3=fVKgr-HaHRe*tk1ypdZ<%BX) z8`v3ibfXy<48al1pk?LCz`)ALaAFb@0}Dd}9|Qc*>{Edp418Xwhh|?eVPjwdjW#oZ z8s1EI134I&o`Q&XfgB9X=0O|`?R88HjC_b~sb4`7CNK#BgoJGn2Lp3`5C_8|kPZ=q zL?=k%Ob`dddXR)DLgE@oLNb_xVGl?`3?ZQ$%)!8%7R3pz;JF2iIc$@AC( zkUlDSjDf3VE~v%F!NBOw2nq#oJSB*LRvz{~=VZwE#KFLr!kUJ(3K=xj##q9`9*HK# z?awXCz}kx>3YxBAEahQuKojFm1F3Y;2ipZEtU*CqE5Kchq!KhZEYBs%z`Xz|QGw6f zY!*fln+($2B7!0YR@W-bor0t{AEd5Lggdim|55tp(*46pT>@v@Q;y}@I7}jFc|Q$GVmRfJbRc?ip>U1P-r0+ z2fIGHU?V8gvK8)zgd2zi9p%Dzlu>{!1x-+dnVW-0AGFkopOryGnOllOX*&ah0Y58) zr~wCWm=GgF0*D<3TC&d8jHDT~x>v-QTZsKIQgna?nYoo1*uX}DYz7VEGB7ZR=y6N2 zf{ccWF@WMw)PRE>WCe;C#42R{pvh_xRc>(<{UAYf{a`V$eo%R_UmRoy#B6Xjn8(g` z5Gk2}#CW`z1=;Q*`2Z}qhKYmy0=i%>3us3n2dEU9z|YFSxQK%TRIFa$2bCUjJmBI~ zfRzDMjPintQ3DVMQG6x{fYgy*d~N{g`=`hNwm3kLm4R_F#Nr8pAOj&59}q;c_ydT8 zuvkF|q>glp3qbl{77GZoGB7TISnMDSG7w^MfiRNA3qTx%#TUTpNVix(gq4BspCSWF zApsiTXIvqQA_i_ktimF;MwDY869dBr5mpAq-J%?4#F-dAfY|=*JiToU3<{#G3_MND zf;^yn8z9Qcz_*)GhX<5l8$g^xjFLQ{GG+rv*&}8aUQpTc0K@^8DLkNzDImtmz;~EY ziU*V-9mH4}KpBx2lo1O+9B>BY0cF4iAcaR61$aOi?}8XB10utTBV{-TaYTk|0I|WT z7$w6=fpQUgh64)@k_^Ydz`6se2nX$9VhrHm02QGJ#90~m-av}b58{v_lp9opLUJeM zI3m8ciVUovxPl30f{M~$b~aF)!Nih5=eIR63$lUY4p|Tshl8xp1x?0b6uKZmcp6ZU zKuQAv5=dzP#Kuempb`k9&;<)pJq>`G7#M{vND#fy1&e{53XTqxGyqzr#ux%g0~;g| zY2bkbv1tHutSM?5NW+!}K!SrX4S0ceM59-{AVGK<5RgPl0}hgiG*AFyg98?&8~{f* zdN}|V9GqzYJYO6ENdpTc5ozFpB$hM)YUpE>16iPQAR09dq+&}0AVG{Y0B$>>H@-lE z80|)|Ah?XDZsQAd!XZXE01||!0R}0gG+-cwNCOEV_Ap5UF_1JcK?;!u4oG211E95J zpfmt!@_dkDWng>-X?Yk(BU&B_(nu|j2_O!l<#7P4j`WrXgAAnQ!2zmV7RazNFy7}M zWOWK?A2mi=0tv#?(ghi$w8S8bNJ|DFHfCA^9X5hdr+@{iUY>w*H%6TT5=5_4z+zyh zf`;*sJBy&TGK>$zIY6Do1X)%F#s}hqqLVm57STz(APY&=;7+1|94iAKdM7ajRH8lL z9b{<>R8nK4Es!8QZ6(MdrL75ah_rP8#0EzgN?iqtM~t)u7Q{$fpk@R{ClMrwo;JW@ z5UVKfByxZ{i67*^Ih_a8IW&-GWdL;wc|e`Q0uW~n69+G-Gq?f7LFxp4kY{CJd_kQd zC<6sh!A5$4)d125>!T?svNABfgp`yCiXa0aeY6RRNG0U~5C>6GegLZ@-C_qNNJ+^G z3O{hW4|J>~G*yGb5m^iro+Nd$QF;O3+W&(%N-qG^*aP+8PTv!pgwc$S4UO5h_qY%6l6? z9B|&_vEI(W@ByljLrj>F!9f)q?QC91qwAm)DP+ZqI=T)P1n>W2;GNaRz%W6Tm4UB` zQIQRKEFH8P5ab57exz}Ekf6{6CIOVedXS*-UeM?s1K1}IR9P7qomkjFK7qs}0|TgC z3^ols#sw7x34+GDa7-D1eW;)Y2@0?e3)C=u2pa9i@F7?b$%h9(rclL)3_PG_1A{s% z1EVY>$Dn987^oxK4F&2*?S>5?iQ|e4Y{(-b;AAPwNN!gMly(NTFm%M$9R>-)3quAC zq{7fZ1Ds|cfMY{5=9U^25W&$(27x#=VCbn zLxC161EU=yKWhil;2`Me8NS5=te~+}Xn_JLW+n@gGRMKdx*BOp!U&YO77MV=LlYC> z;^1IcMw)&EZB-EoWtU+)grpMWZILi`5%$$cvpHZvb`DU5ftnMOK!M@G$dA%2iN!W3 z0TLX9%@WWJ3>eK4kRUuKF3>{Ci5IjGIgvq|l|gh!=0rB+0cudshS8Hz06ao%pp6)n zNYI9q!zd%vpsfj@Q3;gt7<3^uqYqjtNXM26K!SrX6@X5#!AJ!lL3k>dppBFY4uE1F zIxqME#2(O80O|>1G)_T+=#5jb7}%-cvIZp;fcD8S`a)BIf({}T1n6K%1)zcednyPR z)~TRD2PqY7&_R?14?t{i&<<3s42oQeQvoQ+GX_Fafq*U|6*%Z(Nd;KT0`Ss?5VTZ~ ziLERE2@b-t0F)Cj$^wueyeuftWkswJSfGnY1s6cy=8nuwIsRsrScY#+-2pF<5AT2m^ zFa)V1z41E%qz@Ddpiz7UBUT2+Bu0UOA8G|J2M91?WdP+d@N$3#Bc$a38$cY``kMzv zh`c3WjJ34|p7}=~1Of|U46TBe!C(xnf&|g)Nw63=8-wdfCbkd`20oq;4u;2dObkpi zjEE(OnjswE3mC!bAak^{H5m{>J)q`s_;S$NQSN0N41voy7?{c#1tu)xV0ZvxH#0Kn ztN>k6v4VqP?g|bDrZ`4M#G1x+oS<fP!=H3JwPG zl^hIAsZ5OE3&X&c^MemD%znYi08+$sj*)#2(uy%~cwS}XWk(K>B_PM%VC3Ni`8WV% z?@dN&c956BCNeNEfR=YK-D2cnNA@G=>MN!XjHv-t zGVvok0-C{M3_q>Sz#zQ%9S3-Lkm)iLqpo-5#;|cgm>i-T7&swJ0nwBPoD2%}ObkpCqMT8nyP2367^N7PKwHf~QxIT} zKsIfAKjUQB^Nf>0&InE7JWRp@G$;kukpMm=<(wuLc*nRLA8SbmVqGg}DWe>p7+W;x z)FiNS2Jl8`mQQRP-04UP#TghFw9^C`7+Cv}l!8|2$?=J?F8YZu5_AfX_75qxrQQfJ zV+IBW!|P05d`>D#*2eg12UaG=bf2 z1@@|`7SyZqtY;=8+yDwGIe9U**l<)a7D09nc4V)D6lxnVg8WKN{h(Q5bo)VK81{n% zvDgo~*$7=fNDMvGoNf14xbHWRCShj0_59tPF}%xOf%`F){?0L6^f#6=Gy)0CB*};aKaC z+6|ynnH8sSvGwi+I}J>L#DwH|1-QGB6oLj|#Fxsj&P5Uf1)lga5%w^o)yp7tLW;Z~ z&vJm;A{#&snjs2qiaam_H$_8HCq!>hJu`)3HF#vCVlwuT3UCqJ3 zZwTQriDs_mU{Hea7(_q{B{)Pu8;DauJGz>m3IJ6DTei zL?5tlF$hdzV&D(}rC_i_Hi2X1fiVLE=Nn;0h6fg`42nNRz=3aI$;zPkOBhkZZvd;6 zehG~tCPp^oa0Qj^ifoJ=C;Mu2C6(7O?!Fcd=XMt}q%0n5O^z{aeBC=Wr65JfIVG4@C`R6!{V z(58O@8Fo%cy8OQ*>ffX1j_e2wJ$y1}dq* zfevyP0|SH55^fH*6X+@NR4hF>~ zOpLHD7K;_FcMn+-(cN`3TAWi@iqwwzc91P#zb1*1MGBOJKe&Ar}`T)@**!_`% z;pj&W2E{XsjDp6WIT&(3LpjGkb1*P{;b2fqW?~df`oh657smPfg@eKRD+hxjHxr}a z%&!~_*P$G6^N(u>8z%#3o224fL@}2MPE8A-tsTX+D6Jh`P$yumDoSeyB!;iGfZW;v zsa2$}enja|4UV7RFrRfWvLQPSRKhBDFrxYmB#7ZRkRYTbH4F>@b#UfHaxgGmSRMdG|RPqPaL3Bo!FA z#5y<_0y{VuB!du1Y63V(-Mr7qApU@pK~kT459pe3umPZRv=|s94cIxFEEpIzu&^>n zno5A1QXfF0rND}GkwQV~=}NZP5P6pA1*nwBtN9}BRtGDtcqfqfhRVnco0z=q`G4ImE0 z$C%Fc^rNq{89-|y!Ex6Fj#kwNoD6ZGXpLqC#Rgagx(G@#MwS&6E68F(Ssd)3*g+8k z#}YU^9)Q9iQ4t&-0_>~|lF;yQU`Gm%0uToh9+**^#DOW6%Fgj!osnSy$iQ4K)@^8e znxqz)v#&%G<2lU2!@$4+N+lOS+VX^WKvkvy2gDsbpsF$e#96}4!2_xyCxAGl*E8TG ze1LIX}za-!6BATf*@0aOe^Y6MpFzA;GufcK42d<8n~2;?f{ z5etwQhOa<^P+uWelptX`>t|qKknyW$Vt5Cj%EENrO(dXAHk~kCQ?4J|_d$D{fV!&Ml}T&GlLeiQ`+a}2H6EO6SP*2SDYI$PJIyEs8_hp$q;j& zlY!Ti6TF1*KMMn|nE>k*qyh@mz2P+g1h!1CYDT`B*`PEX>ECDK0@*1(d=Tlo&7yTd*Lwutm`iTCFeWqKKj& zbm}99ey|{>e$aTTpsgC&`awe+f{r36`oT^GomqffS%3w>_Ct1^G=Kub61rIHKMMoj zU&wrw0RyBr44$uQ0CB+meDGqe3k>AVS1B+;)>3oUlru0KU}RJhqV!D7!RaU z)QN)=ys?{^l>sz*_X|lAsA~usy^BIyW+i%pO_E|=6F?kr_;4p9b^Ag2j&Gwd zxWqXC(z{cP0Wtmn>G!^JVPN181Fh#}W@X^@<>gqti-AFb1w1o4QVSXdeOr5U-| zb|3{Z$RGSNjN;rU(cH}M0dh0g#s(Hv23|jT2CkM~4u+$>91Ohbi~^DU91Q*a91Of( zi~`U5IT*Aja4_(4GcqvU@8w_+dD#m(%}VrXFMMv~EDHl<7K8&{S%Ahu7{fCjKsqnd zs*vUps4fQwl>@k>&2eR5;9e>Mni2$yf=fN=dBO|=pL#hM!uvQFq~{`fI-rRI#_%N% zI2jloaxzG-U!Rrpg^B!_CTm?yN z1_d41XwZNfWB9X&oDAQJI2fdNVOp>oDI-BtJ3r!NSOIeUUhdoIp4*31-GGj?WehKL zV_@KBM5^FGhDe_mW)Rra2MvUCh(G|ZH4k6(2pR}?SV7vsE@fa~0FAy&-<8K|8X^!t z$9yw}|9b=tgokK>5c3!s2+!ex0Ex`QkD-C^4%347NPz%Rt?&dI2p_r4(LMKxgF#^B zTMmW=Z#fvGw=)XpeuT1LBRqNsoI;jE8;{a*80}`md z$*|)ICxf&RBM+zmzQD=KAZ^UZ2QGmbxDX|<0f-GPffKlpOBfIbT*5G*l)#|Wja~wS z#4t)=upoK~3=+gBfx&{%68HcYW(f?cl?SQ>{s0OeXbG&qjTAlsAPzWuu$90l?J&^I zy?i^x7}$M~+OnX=nY1aR01v1C@f2tGB7Ym+c0vWXb1HYU=33T9##fkIyOu(_R~n)Gb*AS z5(KKyz|&bMzH0z^#DP(qfkDJ^0;s1UIw^;p;kF$E10!OT7gTREhUa-RFbD)r;9!_F zfrCNzAS0yifR{#~89B!ALr*xtv*WU-xk2h-;ROm=*)ts66Q&}@_(5F|*)vEb)G~0{ z!V8^WmtBiewt(CqyB4(%4iY1BejRl7g6vw`l%p6Ed5lFFp2sZ1m-M(M%iXYL~R60EsWt> zPdOQ4pK>zDUVzs|5Q+4soD6e85?A2y4@yal;VYkVGCTlD++mzo4wqhieph#hT!Y+r>-vPOX^$WWgO3w-;2%qG@ zbk^;`;4B8vZQS780vc;%41fESlfm#ACxfgtBV-)FH42nFq2mCDycrl6WzTyvFfhs9 z@Md6OmIciOvoMH2PLxJW2t&ue1U-s57+Q)s7`UxC7zJ2!p<=L2s;CJGY_Mn^cq%#vXnKf2xUnVIT%(>EGO!$}Y)l^gv!g~u-;$&5Qpm>szo0NpOY9WKnlAh>H22l%9G?hH0YL8i?d z;FGSo8JHOb-C&%0VMf7)n>irvGGb#Ce7Tte;;wT{jDosbpqzGLM!}9P91wT;u`vo> z*unvE*K;ODL6NOcPO~thVE$I9Js_jcZbfo|#5Sl5$OZA+kc{5D4ao%@+o3Wb7x-^S zGJ5THBp3XE$$(s7zXNJC$Wqt=-`p;IjDoNOzPW3-83pI=fEo?*&+i>bZgbfQH2~zc zP8bJd+S8p#ZqwTZH2~zcVi*Tx+WB2bZj;yzbv4Lsu`muObaw8B+5<9LY!8wP681o4 zKrUFe2Wk(xb6@?NMtAfrF*MRI}FKBx@H1r7V4_J9K96v#B$>hoGi`(rEc1sA-@u*aPE$Op`baH2{=G6Awd;28F>g7zbnz(-EXF@IHbR1`}W$ zP*A-&0<{zrASOqlmV&%c0poxyy?hjEDJVdsk3lU3xjG)k0a?217?Nqs$B|6)fN?;k zEjf;4+S}turWu`pa@vIvnH;psg)yACCi z46bIJ4BXLzjDpL}I2k^HIP(P=1zF8G83N2X8Mws+83nB*SRjQ8DD0XgSRjQ;4L2iN zjTR}*#USj%!N8r3CUF^40#uKFFL6sx-CSfVI zzd>O8!30?4HeqSD59opo!aVGc(FJR{Wf>UQR6r}+z}i7qGB7Z3?-G_~6Gs>95awa$ zLKg&`NXfv!9jSwG7ijwgcdr0=L;-AQ4LD#H!UKlGa}5JSz!p{pS_Q}((4suB)ePYK z@3=t$avxm~6d;$-1t9?mbAobt?VUUBR6pqX_ z3=9o43=nUmXagr3P=MH>3xWc~5M2-wAVo;RpfMwGfTW@ef&wH0T@V@|y1JMtL@?Ev z6Ova!h0#H0PDnN1E{qt90G~1%9uE&H)=s2M6$&a`Xq2gLAl;k_&r~`{SMq`dK|v;u zE(i`Xo~Pjq3>!dk3d&6ACn0cyf(rR~SERJVIbD~L;Q>fzuK;)4FGR3`Mt8V-4G|d& zatqQvMBuDM3LH>domLsl3Mngqj!gh(Fwl(^&`iew76b*30=h~_;JAaXkle}&DQOas z)PPD?aNvZX3qsNnC!-!Cg9AusuK;&8Qs98gP2|AY3C?74^6*S{at#AR0az#YVwe}S zoMHh;!75>PdGxs$ZU)L{Vn`^{pCFBWfWrioW3QtNfd^|22e#j&?%tuU6_gT4aqBg77l2a2NpwL_O4x%g z2u%rBkx~L^{X9xb1diH90879P)c}# zE(l2pnn(tKvp*;$$e;^?QUX7^AgL(yo{K#E%f&|LsZ3C`$(pp;;SE(lEt_WFo2 z0hSU3{k%9KT?J6zZn_t=`2p(N-GOmHZ4nD^L|a5qbPWq)Fd}XZ3#63->gY}J=7bD6 zfO>F3>sb(k5z*^eATrOH5RI8j;DX3X0bV6(u4Q1D07_l>lHUQ4fUmdz2smTw+t)S#bf?IiJBrq_1 z00lfK*=<614k+0zMHhr5JI;z}ZnTS;22}%WE zRZ=MZL(mnap#CAaIywMSK$lbi8negfY=UO3!JW-mq}&FY(*>u3a&$pZD#$_?grtHN zBw^5!V{lhu9+DtviUr&RoQN(+WGVm+C3AxsCdh6DZ9n1$r2=HPf(6mt3KoR8bv2R; zz@7!AhNDP=pb=eeP-@tLE(l2t+^tAWRA~F1Ti5_mFo9-NKvfjTAaM2qWempfFl{b| zx!PO|+`)`!GHYTU3C3Cg|v1u=f}kz;P}nAb=PRhj{r6lCcx884DU; zVhk72Lm2x3tFa0bL3JAg1H{-2J%nptVlx&LzKr1;kc@Q)9W@AbEjTfvxE2y{Ecysz zoieZl0%&a$W4Mn#!nN~|j0O3H8;h|Ekc^#;%~zt^2GC00N*EsUkhxDM zL%0kZs7nft6b1%{1)#wUYheMDDgay~&JbpYQ~{t^rb`t7$~qWT0C?aCG~S5Z-vebe za20^u-vbMRidE#kDOeCvtPW7$6dahKDgfE7U_o@Zf(0RNrMPd3SZi_}obwhU;-3f9 z5&Zy)RlM=;@M5$lN@Ud!PNSfzf8cwfhiHLXyH8sG+J*18}h>(HT5%J(MLdh7BRzR~z4?rmaS6U%+`Ute5 z3f!U}_~|1=R+$9$ej}3iL32e4FIgG5sqB4lNdc|KHK4@mBfR^4L z_^BZVAp>ZO9WvSDXo5)mpxK@S$lf=GDh1cju(?wQs33TF6gGF702M@O%j7@>;js^z z<|%;+g3Sb#ZJ=o$6hTn?05r{mA_$qu08R4t|-X+$P+nWL6G;6Cvw1o5buK~avWgZCuJfB z;r&zKM9*Xj?U1;jc1S=Q)3{xPQP<0X1Togjffn9D*2}S>?^XjFFyPzO7=#W$(*~p` z5D)Y617SAMF+*pevY;i8+z*5y>mZQ@;p-q_f?zL$&KY6@oiijp3Dm}BU;wRy1nCEF zMpT0eA}@s0fQq3kgoFu#(gtiHqz+UeC}SZG8nT0mf_lQRgNA&dVxVy%=s`mu$AN1* zNRN}hoPhzetN9i!WYHgvKoLjy;et_sBDVLD*m-bOp{^VMZ<|=+aJZSw_JN z2RPx2_86G{6>~6%u$FKztl$tWRAOKNA0NXYYHYyFaDO5b0}F=$UkL|;dkF^v-wZ~9 zCnX#VilrP3eASEs3#&L7?o@Fw@cm#EI8npFz+B70z;~HZfVY-|!Q~kz1K%V@0oPg% zh9;2s3Pyp-$s7!uCv!0HH8BdvP2m7vW6gJfQQ+QO4hGNnTnv2A7=@GPaWE{N$HBl? z#mFe|Y5@m>@<~HCo6*x#AuWQszFZULpz`vB!+Q7HK`y2Fv7v5W0CFbG%@;r%sGE^@h=JUPzCQsZhOs{ZEQr280VIgAKLIQV-Jig)3v+)0 zXh?0K_9qzZLfRp=3n{rw0CB+KgKd8Tc)!*GklvkQhz(-kTZ+R$cMec`*8uq7%nu;* zk3lvfD(ptuh!_CkKsF+RZd+{Fjkr_bz;4LNrQo}W81_K!B7$ch&=MBLaL~R(c-n!S z+z2`+8`I6NNV>rm?B)QFm2V(!ZrFq5<_#bY!p#pL>cE%m3hafr8Rh(K(1JYB$;)tu zLN-Q%j@AcT{UjVBk{Zvtdec)Z16klTv@5oI8&+>#T zaWPC(;$qwZmSsB3lX&9gf-5zFT-~*j054u0)0Eh#1I>Qkpr#pZ+ zh$Pu?ggmD|0O>=!Um&9bs2(ji z%F4h8sZAGvn9%Cu!cnB+k>MD;%H!GD&cI-BjFo{epHYM-eFpED*)C8-)08$^$s0>!ma2%?h0b=lRRtCO8n86?p%-{_$ zg}4p=08$^$sLTU$NWuw-!EDnM5YuhoU;*t8$9|(d15!id9=K%yy8Z`z@6Ux3kjrks z_x>=PM2c<$5CijnPa3O`>Al;{g_g-s1t@z!?AvzB!Ci;096y zhy&e1zX8OCrtk-+kW#q7X?O}p*}$m|x^)C?3q42-qk#k#L~kI01Th*&U_odD$>B6{ z4J42c>9K{r;54Fvv;Y(&@CGD^gR_BzvPTlWffLlyhNUmi7IMD%jEZa)NO!b@=5P5H zFp8txx&ad8dC0ta0XmP zKyE<-vB5`lqujRuYBFP7vYj>D3$zd#X7Y_$(QjK%G~v4=s>0aQW6UG6?UP%mF?( zkna>Dqk!l%4u-6091MJhjKam!I2hat7#a8uFft0SnZv4ap0j@fXH$P&E%%1u+p+fiZ!a7=jvpoDBJ(wni``BZGj1 zKPS`)h{YnHa)B{CSec8#HJpQiZxbWW1_?%n22cvw%qYOSSb~vZ!&z1ap-wRlq{QH?=ykiCldQhlSC$txwM+)r==aE92;R38m28Xu61*FRr5-uQx_5=_I zcDcfV3y9GE0Adfp(1zzH$bliCTPoqrxE^rFa*Hx#xSa0-BYPCm$>E??(R|k!1=vb< zfMN}TLF=~ot}{w;Bc~*gpr`={1B!Og4PksY7)2Najzx1q3m8U$Z!w(EGKEo~C=Q;7 z1y09tLQ4)t2B!UioD3pI134K4Fh(1y29DN2M?xS->WpmYj$C}_(HV|cI% z7j!TYp5{P9N{r#|Jq!%s6BHFLLI(Z7$B|!zrkJUqGc!RPaQ;Le^keU3U=TPH$O#+1 zgc}VhVO^#&FmQm(-vBatu>jbb2O#!jLGV$X0+*1iaR6~3*03h*0XrQ`fL0lUTDHMx zV&Il7`hGre%l16dF-M^64sO|2&H|eTCcuKwmaTw&6en!B7Vdt?;(`E}`wK2X1_ME- zwl4s&q3*u`ax$nb2MQ*J%dm{az*>dmR?wO?&|qLLni#mP+k=##Kng`bZQTYmL2z4l z<6f}S!30Pp=(s4*q3;HlS;5Ceu_LuCKr`W>!N6`LKZE*0;I?ikQs{sMLB~a<3PWZ& zK*oav!N*0Zps6HyTofpL5-vm9x}an<0Td*#@HqhDfa4eA%uUd7QJ}Q+0i<`Q7-BEh zZ*X^Uq6!yu@Edb@?T9Q}Inr@Fpj|z9ZYkyf9lfq_1w7OTzDFeB3bfA&I%>TE!~yp? zIacmuVAudr$2oT=1H*$W(3Tdcxgl^BG9(DkVxT)H7{gDga6!lP;pwOY?EaXk3=Ehq zCvjj9kS79E8gmu0gJ00=eAa8pP$?AeV!p7lc9O9;mGdcPQxm zF2?ZUi3|+jAW8rk@E2myglk9^9RP747I_IXGJF85lVqDFiDfhZ`K&llnt~iG$_Z+n zC0vIL44#re)dw1dK|g*TJPNZHO`#BE6b9rEkbf8&!R0rnD(b-C2aq+efkA~EkSYw+ z>J7L78J8pFcrox0V#5ujA;b$HQ$W`>fm+-OH(43@U}Jy*HxYI<+=ST0z`!8j70U^m z0)jgoGJ@c)iWmrJacC;Ff0Hm%x4tg1@&GofH9+W#9vi|A7oFcmz?0ayle9 zbA!hJu;+N>BmJ*|3&dlpT(DV7@YPNaKvsaR{|4QHA@CR}mK{JGa4hqH?y@L&3>^jp zU1o9MF*uX4fiANE7Xl0n;F<$;sRhbSY+(06hXFyoK7l9DVL;@f1~j+_9tK3d*bO8I z9tK4A8AuR13@D%y&k37y6-bQdgwNeFrE$XMXyFrCf{U1Hc$nNECxD%oE_W{fr^3@!e&&F8&sf{B6@=gB!)o{fy zPY*yGaN&Th`NR!sK0)K10px!0S#QXKpw>QksL&N@s1Vfc1P>M7M7ll(BnTcV+=0}5 z1Z|PvX<`;+Ta6}2ouNXE1b&!Nij)-2HW?{-fSP0Ap~5yaLGV!FW>ES03_6mqYzG5_ z!!vL>!V5|c1t2!0Z#x00^$Q*q1YH~$gcNvSLGVZb$Vgb=gWA#HivvMMBMX8C9Y9u~ zh(WAE)(jw#<>j#U0^+QbL0W~uhJcAAuf*PGBQBMxH@Bz#<+wR#d9Kt3Yil);X{S+ z!WFc?j4|9@4L;($=AVx_J76j*glyq(bDjJV63a}xkbC4h?owFgQUr=o(v=kL8TPMBWY}-u>}fRFb2l}wPFjt zWFJ(7GKQ~JS(L}7P0~HC3;h@Vs;lqlMb#gNy|F0ilqigNvcZ zd&=;l9PbGl3zFfb=;&Y>UNP{hR&Yc^k6lJT-V<~}ImYpxpavprRVz$0sIiS$)d~|u zUeyW{Lt53!z#y^>JktU`fRU}4iGdNZ;{((PW(;5Sk`sJ>D!0EdVzC$xxJeD#@P}uE zpA}@I7cc0TuMZ&3CSfTa&~`fqYgX`ndmhl%wE_^QL70aZw7qTthy&iL#sk_`cLBuR zB`nPYKI+AWl>xMqniq7$ivfrO-bTd>>K-M4xEqCqP>weNWgWDGZa_j9R}O#$(Jwau z2||zH1bbit$c95u4_p9ojtNVHJ-}eg%D@ftfB}dD@c?MAT>^-EN?4i)bpFc(5T`|$ zhZl72%K;DvysaASfe#=q=u8^)LFF5wh!Qt-oij$%E6(<9CpD^y=SFnQw zKaYI?1496avrSl$t4ab#rxZD{A61@aAu0|{}E2NFQsLr@Pa0C5Nh ze>%8u?0m(^unSM{W8KEUvjr5`AZtL;a4vv>LBI)`ZVm=8FgSoXkWC1;0~iKvI-{ik#DT;F=xmk+ATG!cpfg!6fH)vO zfX`xaWo6)oBqdPhH2`tZQx7Qrp`{*>5Ju_&3!2b6h18|5Z|IFO_S^1uNQ7ia2$hx;t> z02Szb4BX-V0b~>`+!fp*k;?-*`k?^C0i_+#kq-+%9FVg?$5LDXaUjkHA4lN<&BCB# zC=5UxP`HDRpGW|45aB)n#6=HxP`!;7?jRwIa0d&bhdW3RIouC`YygEj$O8uLhD@60d@``4#N2bATD9&7lEA*+8d75`QWnv7J!U`rTPmX z4#?S{UC#=BP-j;MF)##xI3Q<(&IW4$aUh8XbSBsa5EtZZ&{<#)Kpar0gU$dG@JA_a z96((3R1YdJ&{91}2qV>l1<_MINDw*I7l3R4rFxJDHh?&w(gxOtj20|Ei?(gt); zm;;ClO7)-v!U{kfP^t$V47LEoL8ST%ATHrlUjLF z)q_r8EdX&q&Ig^mx&Xw1Bpc9)s~13AQ22vSS`C6G8_)@>1|SY1*(89txRcF-K+r%A z14Aj;`JfG>VCV0L3WM4Opi_jv&OZqi1T}p@hueWO?F5kdpyUJE3wi;>0XZLhJZLbq z!UY`*Y5?LuoDVtXJE3Q#>l`l^$in)$g~g+1_KUJkm%FRpzFy+8$&o4=Bh9=!A zhJ~gQCrmZ?f@9D^L2%~=a--#CD=r59d7KPRA27PxP9Fp)xzW<;gTMfHx6!=nZ_UMU z+L~)5c-6#)i(!M!i14bcEf>Q~+u`X|uG9&f4EHB+GB{~6av99#WLPyD!Y-e~$?$Ow zggtjICnN_?X5?a8$;r^P5|o=6xh}%k^BB2&*KjhNT>}xfSFj9fb|ax(Z`0&8Gk5Xp<<0IeMa z)it2Pm4iVPG{y-k`4}0LKqBDc7Q}~?vI}FesRxy-$m&6ShHOK!N9~IvLu;OSUo86S3=Z-M8N7nd~oE8d`*HUmQqNQ1(bLoNImq2ZMYg2ZN{$qhN0%2ZKZt2ZQJyHfRo=^P7`F@LMwn!_pQG2GQeejDqvS zIT`#SIKdnS2CiA{91Pd4u`r10GYT%B!NFiXlY>Dtl#!7uaV7`DT@Xi~k;`^I2gA3S7JFC!r~x01Y;6889pR(LgGLqiIZVM64*vA>olk;eMUj2eoltf{hSP)aQJ@Q2IKk#S zL_^sIj0_@ge}Egdpwe!J9Jl}lm3F5)x;Tpp?(6b1Z8U5&&eP-c|Rw^tNok| zqTI}kf-g^VGB})pa^h}qGHks8;V>{Tl`V&bs(%Rw1N&Y^2L8*8Ty6Uq8E)@qWZ;iu z6#Ur6!Qgp~g@Ip+k&(;eIt#<>>nsfX?u=X~PjN7?UtnV34`Jj|KFz_f_cR9s|5HW= z2BzD3j0{ZI@)#MI{#G$F2(ByRV0d4~!60&xkx{U>iG$&669d897b{U3pFjXMH3mgFqC7!@wYTei{dZ;B*cK5h-p)!Nb!z7?@^oFo^u+Vq^mC zd=iwH#lcWIi-SRgnVV7YR|+RXcq%8D!yu@=kb@zAAqRtqA3LMq?u8r-zd)S-?2JsI z3)mQ#z~$KMMH~!%i#ZrX*f|y0(_|3`*F<^}hC&LFBPKW{4vYZUn zvYg=H6I^Y<$?(R46B4i-L7WV-pV=YGe1bR`x`Q|wME0{osmgk8z%!(@hv6>LD_Ab z44vCJ8AKY`83mUgN8;pNL2wut1l4zNGGy<7y7}!cP6or>P|o2!oDA%Hp`7+(oD3I^ zL97v+dxDeU;R&dW(@7*{ewR5JreB6COS#I)u;MCI8Rs>GGDztlSa^Yh;l>5fyfP1? zpam-@!+cOcF5&?t%%@e%3{0P@m>IZk)pIZyG;lD8&tv3LTgk!j$B6~Zj+fwsvm>Ms z?9O0LhWo*sATRpcBD49fYSp#?@|u22pJ21exxU`nI{(&vA`$iQF)uG^L3 z>X{fI_41)SM$mYGbtXHch+z;dGvFL~~(G3DL?tMh1`*STsZ%V4@15U3rWQ z3LqF-sART|Z!R~_V5Da7iQ$wa6N8y1BO|=IbO5|ef`^-np^=-5!E7=UMsvw*3KK=mC6N9BZZ0u_f<-V@ z4zkkCEP#cPLGXwwCxed~Cxh8{Mn=I@H7Li3iBV8ios(g%Iwyk}GZUkrjs_>gHVsY& zvj`?e!5&Rc1{PyZ2D5vNjDphIoD3_pIT_5vnHUA*bT}El>Toicr758hm*nV4I|=C24C=~`ML(kb~-bA zlui(6#fq6d>S5R*F^o9>K_C7a!08Z;5i2Tsd2 zP0+Ob6g7fDH!YhzMT=mNAifAjK1mRymQ(`}+i29lj#~tC+-odR@tVS@cs+Qc;xyQ| zEX~lUScN%YV77{xvV^yad7u*>gNTnjCqo7Ya?=acsslIs1Vk5~V_^scHF^|;3MwHJ zny#D-42 zT8JRnqZ%wiV2>gT!aa&22=OQ=Y7g|YGME{%=!2v7LqAf~DolXwD&hf!NWcWJLP&@- zfH<%a*)RbqL>_=Run^hY2yq=a(qSQjB8UhPhl#8Vpv_U>5ZM6YTw(`@2pcG=euk#% z84L^zX51{g;3TUsiIu?&mShtq!ICU!W%3biP6n>E>YNO3Kn=leMy?Nfa5e+eUXbir zZB7PBP?PUCBiA`YIGce%d2$%IH+LOOi98Jl_Xj||ISx=q2hufy^ycvmRmOK{+r2EO?5M2Vn>!c=eJd zKh%(HCKUB<3=9mcIZPaE`zM2Y`rzgasP)X6%f!K!6^<&%qQTC=o{n_91-OA0#w5tV z!1fw6<`34+!0-uVYAzE8&xbP%3=3wkGH6d_66LXDWMsH7gO!2RgNXxNd@#&pWng7x z;^m1KWMnXy$;u#fi$|6hv|%y<#DQcEP&qRJq>ztE0$k2O?Ep8dK^ibOY=SlNdNVRI z9Dr!#0bBP0#1UqKlvbpeqA-hnxMn_w5KugFtD+PAmRhG6%iase}5tbL2GriH#2dto%Tiu zf+`HqSUzUVZDs8Kz>J(-U z#0qa=aD(IrKcq{}>db=DB?ld*&FaE}+9d}G!VlAig$6in!4K1hiGpS@K>7!`OAa4^ zfcQ2>fQuns3qrWT}92z?bIND$WI5!|oF$-t`4$-wHv&&Z^s$;lvSs0kVWV4cjz$RIdLg@d8# z2@?Zr855)6bS+K@=OPoMpr#%tgFzl61M3-1M!3TRz$=%7g}4|%X0cA<<4L~6z_4Ks zD+B9fJ`T1CpsSmqjsxvTW)0$(VrxbdG&5!rVG~F4FG!`?4@P&CKmZA{vT*C71OiwP zJrF>G7=Zv1garZv1JA013=9uIt_#?1%H9hg4mo8n!vamSK|PX}-k3 zAh3{?f%Os-2amKMBSQj+Q_jS}1KPs20mRW{5@yR0M7R&!3I&~Ws-uJ^2tMEdr6vOH z5N5Sy;$$~uM5qM|8Z)u8fvf<#oB?u7Agc+J3`tgjs%Dr0pybAC!X$uV6=-`Hs~HnF z*f$?QzAA)LMk!mK*1RBm{n~RiyL8T0948I^72U7fk1%dIujPq9u9<+%T9R#Kizo#JZi2 zhwlah!vl~nckprWygtalAh4K~fwh)jj%U+p28Mve(4y`hC>MY@(4y`Fhz%?143;1j zbrV1wSo87&h{MXnOG@JmT;wS%1(#f;H#Y+y8X-kq1BgRTk+%V&5mMwm0C8YNp1?A2 z$whi|(*dFpXOYLldWeCcU>Pfe88;I@kHQTGh6^ALEF~H&XJugB!KcLoD%vJ2X9cG~ zUQm&CU^y!TEafpUFkmLKMrtH7_8&+kGHAa(YYo2?1B2j3BTk0jMw|?+@A(;-3XM4# z1gngpDeo?F%4-FuyemRn3?N0UcldZZZZR-?0Qu@J9|w>8AqED66|4-bU-{*DE}Ujy zXjlO)e}xV)FkAp}pyjW^N>&ClSovG95~=(>0OG*PUx8Ju46Kxtzu-a&z5E52M^rC= z!G#oh`3o-UVdbv_$T#GazXcGDSW+E*%3pBRg0K9wJjB4TU=_6dopplaX(1@+Lt;-J!&FmC6P9?(!x~lwp(ZXKp4Y()3Wh4mWLNfSUOPa@GtKY|$;E`T&n=HlTI3SnSi zSj)=59U~~f%NoMKU;yGs2!aEMDf$TugWw5sXaT@1h*b4l1Q!72B3uk0MXdh>P@3%E zjP+kY0;S0g7DR8dg9I^}>>xpCZO8^1@Bl|I0|UcyP}i7IP?!xg@PQ)e!NkD^8rOgc zf=mDngpgcQC9Ea0rkViJ2&t(KfH>sTR39K3AvKl4I#vc&SWOiG;*irgZ-8jTQf7b$ zM%W~g8d9KB>RA~Dh1q9=E(d@HCaCC?I>9Kzz%HPHXmf!E6TnK{KuHp+6m+l=NDz^l zK|>{s;k6=M3`{3E7+7@#QPK&h*~6+UD36j(z=G)M1SE)&PC$atbb@ZM<`KmOSI?%z^pfN4h z*&c{U2Gv?rS)a<1Fdx!egPfnC(E<&T(_pXWLnXn(q^DKEYjKgq;9i9Zf)bxLXe0~l z)mKpMpgCRE)2d*v{)CEwPQQgM#r+Kx11-;mdle=KDly<*T>x<`s3u38pCJSlMLs`6 z0V)RSfk1DC0~ej3P=$E49_H00n6sv=ODLN)1?dO-7Gxa=Bl{M_2VoT7f&@Sq#kU{< z=y`nz-$Df$kbMgi!{%Ga3WXam-+n-yzy>WwVEuqLfejMGH-U|Q$syQ)fnT8jAF138 zZaiqWLdRtfGNUZY08P2G9%4palmQYX*2|!4BESY9d)beHfkC^DQHpIN(s;BF0|Nsn zL9%Gy`bhIM)U?^i(YUJrc(66 zi!6v=-?lEabE`a%VIu_qfr_i_42b*u1+M#iqE`%Ak=|YsnZMx7Pi`!_JZ;P?` zwwOZS77sSxZiD%@28(ZNDD-X3fcsW((rUH*XVS6x2D6#QA&~!C~Oa*~iHM+OXjg$H?_@A16cYeoh9LRK#T7UoOzvF2N3H zMrjq|k$A_zAi&GUz}hAxz{9YOfgu3I$rNJY__2wBVFE83gP9Ij+wU~u}x!gdEuOllV^3)^`l!RZVP3{pC*ENt8^h?WcJ*hs14EHZ4@EKmjY z;~2%cPaz3{MlbX~ff_Xo3>+YHKY$$bnS}>roC6;l1IQR2kTDH>Yz!dnoFMH7Kpe0( zuqFn6HU_6J5KRI6$eQ?(G;IKJz?wjt6E-t2Jm6$i`8#mK z2~215Sfd0vSf3+J-YLz#E+t_Roo54b0 zVl`+xEjP0WI6WSaVq{)| zq@l?KlpYvPkjqKo-eM3UXM=2hf=+u=3$8 zQcH)z3#$S?fH>%>7AcBBSq`+l_YkzHR4l|OD0mFfRN{Je3eoyWIn9aD z`l)6GwSIDtT0hR;85k54p_OUxW(I}=5T{g#g`;sZ1H%DDNM$de$;cp}#Kz$CnFZX& zNKk^*+u%0F0wp#EaMOeXr0fAm*%yd12W2E>4a!hupzA-`_cAaXP=;C?@STAnKm}Td zF9WqVRG@YEOpt?AA@#NhXu3!ZT5nH7s-i(z5R@X>jv!?KkRUi;x`0Y?NWd|G1o0J# z;7Zv+4H`Y5O1VJ|OJV|5$B<0NCV^BZ$HD4kMUYFOR)PfKb@B$7(V#l{gBntuY@p7D zSk79Yj#MXa0C5m?@&k}UvX)e7fGcxIo$LVOkW(iYKr}*TZ5DtyusZnyh(k`D%%I7J zxugm-qz75f%9Fc`fx$o%S|?|IXJ9DMgx1Ni-x(M-XhQ1b&!&tFA3!E03v#d_*U9pr zMK4B-xGUvUP}vWSEzqEWD5z2fB{l;sXyHb~#8#k%l-M?CVM%N#B^Lt^$YBpa4g*!V z$PNRwG%<1kNDw0zfCMpe0Z0(qFaYNQ1#KjUC1?|L7=z%n^UzYMMu<`H#wDauDf&91 zRJwT`vsBUoFR(m`R4U#4&A>208(KW<12x`3oH`*Ej!l~x7zA{{r4pwssBQzXKeK?@ z6F}@Qpel<;4^+PBKn-~In}NYV7h1kt2UYvJ(DLOJ$TU4j`C`47fuRA!2G#qEv6U|u zv6U}@B$O{~>E?*0J!myDs3(BZ-UrP=fQuG3Riu)o4O*MCc_5W6AVE;cf--OX0@P*% z?I8h`FwjKJ01|{RoZp~_RMLFVLn>(u^w}7Y7H=l#g9{AOTmKUv8X+ak0T73rlI8P*!6tcz(a^UC*uoWSdIiSf8aG3*2fEx^;WzK*nK#-StKu&r9auTStp@WmG zK%pMWE`yRMP)k7Q87rVOv;;R^Vquu4z{$Y+keyLb;UWtI=p-6e(7_)cCU7w5fHvoR zWMdR8T+0sGfAE=|Q7~o|J4DYSc1FQZtJooW_OUYxK77c*@MkqU1M5dNMkbI+g29g< z7eKQfU}qHcdkp0;axkhobV24#K?CHB;gW(};Hza=YuOnDr*49pSIw@3>kmdoLANU`4Amgc8%9Q^I1dg6kqCLvK1R_? zSD6`dLASOth#hExxEhq^8N+XIb1{hWa4}d5id{gFP~hQWhyY25L5}ob0_|Vo5>()1 zn5n?YU~R_8!14g9As<|H$MbN3w>4QCfNzQV2$cjaTCg_c=9b`PWUy(5SPd$jtz9Kp zcQP_EghRzZp0>_aWd-e}go%Mx3R#=;vVpc#B8!2xR0@ECzQ}1ynPNqfi84jzShhaa0@B2oy&>fC_>gl@4(fObppk$YO9uJ%(yVaTJOm z%u&dKD31CFH3IG^(1v5zR!H=LrdO>SB*2&WA&Y_TgJNI>sq}-Y1Px`uR3eK(R6Y`7 zWC($(1Y6V~!FCl*45X3`w80r!q0ka;4mQw6XB0u$MrW8HIQ7CdIwK3hdvGN=nc zE0wI16~Mki76Z8eqH+sVB`932iy$hI#Ski=Aghd!0h@^|hEVwusuC2k)=-tmVhEN0 zp(??)S3}H17DK3HX#<(Y0A8MA4ONLO23EkBDGjN>T%fMhTjg`TAn>a_TH6ug9G*$+SBw3zImW&J&rm-^U zr!#W!9JOR*H~``ZF$%CxMVhq&rEYy^M$lp{299?Oj0_(@Mr~K+dC0)XpfH`4f%Ov` z58H1BM6U>R96W0+I}iKGGq8wdU;r&HWQ}AOMiB(v;or*1ew8A4zND}(h;8SWKG^95!M3=G!0mAMZgiGgMcthdNBf!0^BGIF4XmIi1YMe-pr?kJ>HTA)Q<)`uZQ1}vySOE|2L zb0D%kWO-pGbkV4_GI%8=Of_gFrM0pIcqJvW7kwdHjT0DXZC&ut!yj%<* zb*!(%CO{=XH9BLs5g!)=NEhor2Ej;0PKJI(P6k#vHb$WZP-WA>yZh7mxEQo+nHX3@ z8QDsC85v-*7EBBbtYM5SY_3X-47;FOKvBc0$|k_}4NXwsAd?{bkzfXf{ZN&l^v0ge z0@}X@5|-?M6#bwAfwhE9kAdC%7bC;(PKYQdwSeqmclpK0zys}CDS?u47^4(oy)(qY zllZt84)Jj@u+~5v42wyygKMD9bC)E!XEewkwL%? zu4R;B5HwQdWXMzHWMHl3U{o)F`bUxlH1>X0kc+`um7RgLi-YY6A0tB-R2H=IjkSkE zo(EL!Jpk?SZ{*--1NDeDKvjTCv40$V?4T|YvLL9X$iTn=E*#CfAt4H?c3Bs2h=7d~ zn9a(-x|o9-Y+M0U1&VRVf)L}tTY()w8kckMg0})UfH)pZ9PFSxJqc;N6)b zP>rCLC}?K^*wsoq-t5u;5~3!ATq(U_qBYB$d-RSQrFjwKy4)RX7+} zRhSqVm@fTfVi0)xWpDmfU0V!0Uwvn!#TR&GYYuaz7Og;g93 zLaVtL1)o)MFvM1KK=kz1axe(gaWDu4b2AD`*FiZA+>C+?>o^$1>NyyM7H}~NI@NP9 zoCa|Wcp157v~e&zZsTAOieMCs>EK{c%VJ~@65?hQ+}FXuV0M*-L5QD`k&C61gTbeh zgFz^hQ828NgJDVzBgB@QFb>F;_%05HrCCg1Tez-taWFjBV*(p2-_600wv35E$dj8< zaCJ9S|6*=NLE|0{hSKE_ErJIpaxm~u;$RS3#K1r-({1whtf4hDBECP<)tT+G36$ATFWPPt2v!q#Xh2Lp36BZH71H>2S9 zWl%k!ux(tML4ysogW$PU91Je| znHYrRc^L)kBsmeBerZmI9}hUdG7N(ER&y{sZDC{(`oO^`Xt)Mpj9|oC4u-{RIT(aM zUX)(P!O*`B8k1kvK^?V%n^CY~JqJUqHWS1nw>NMw#BGGeaot8J2Ne1&n>ZLMH$mgr z{4589a4sV_Dg?`KLgUq(n^Ew`O%8?BNOO&QNiFlP+#%! zG77$gafEmo1(P0dKzs#~5qb#a2=Ov92=2Jc!SEks=4CNPL796T3{BaL3_=}ZjDoZ8 zb1-N~b212V@G=VS`_I8pro|3PUkVJI3~3CU;PfT9iG!1&g_9GKDSmQrGSq`OpiCjk z&&iO$&j|^mTwzXzpTeBrNDvGXzE-nTkFFusTp`ZqokT)OdsVX2r+$|`S zg}*^7+Qd0nLCqPcJq(~0si~&4i zH!Fi_5DO1zaApH2SQc_|fKQ!!0OG&~V_;fA?F1p%U<`^NY%m5{5H_&DzC-B6gU|i6*(D%To@SzSL<>z z7=|)32p!~M6f`j51n+(oYT#lNd}GQ9-ghcAk&99AyB#M(wmm1L&hZc6WY`?Q$sh#E z`uhSn8O#GgnN^HYP%4I#p|hS5QkrMPa6&3NPyx=>$;mLSlM_-M?4QcXa6p_1Vw&6{ zPKJUF3=Bf0yo`c^cQ_eR?{G2*39>OVh$Pj3PsIk+?p%uS+WqT9CeU49pxV6~QoB#7 zV`2c;?)fzw3|neAAhrAS1sn`F7eH!v29dLC;btMV6qp;~X3c?^1#cL*zb7s7mDAhb#uqeJFy6+~)w* zZwb0y8k+lHVxV3!Jog1a6@m%`Q10Ua<-P|U&|~#LxlW*yl>w68*c#Lr88V<6Kpg?F zDGeg1g5YhW4B(7;3aS=4W5UExGNwZ(B4ZYS?1N>@1t1PAWB!2}hmtW-1YsEySr9d2 z{)d`j4hk4(#)OH1cD91O1ZvW=R6wE-v3PJ;BlNuv~8B{IEC#FFx?3W}_#UM(V&Q&loaOp`x6ImuBm%TKU-OR|qB`U?q z;3LJ!V5)-HWePdP^&LzVh5%fREJ>{n_xj$>>#T|#LgA8Q+G@=B~iw* zz($~-egzVQ7{MBdPHnT2gBl5ap_IDNAn$v2=O5s+`3kz5V(HPkl@3?iq0z{^iH zL|I(f2`@j_Lds92DU9HlZ#;uNc|3odS>WBq+s4p3d*TE)%) z?zAI%>I@>fHE=nw%NUek&S4OV{{fc+CqV`!M7k@LhRcBy8-o%eh28!Gmjfp&1|>vV zI?4{dLloo|Z~|gbf~6V;kzayvxgby=Gbq851cS(TTeuuJ0vMEFq0Yr=$H@?4$H`z9 z0ljH~E3^;N+OeC<$RN^koE_$SaNse3Z{Yxi7z2aG1nB*>pyGuw-1#0SL-9RM2KJ3i z1-dZ9bV@lGHr(Q15SYU#*jUQJkln<{AkfXoD7gC;2g53m#4aXAuH+6*h73MVu&Ny$ zoD9!F>~7HER7{IDGC-Cva5+^#j1r#4$aS=ugF&o@gF$#2qu|Z?91Q9UI2eR?GBOH2 zSpu;G#Ni5N=VVyI&IwY*CC&%20;FRmAH)iD5c5WkhxJ_cE3L}FgC^NEffbLyl5Ix4oz;GUP-7N!ymgsFp z28IU^4uc3tMt}o+S1veRi-2SWAlIv?8-UvM2ss9kC$XSYiX}KzVdgVPv49QN0bg(n zHQW}$ffz2qp#-{U7Gbyq#Bh)x2LnQmK_m-ecp8S`oe=XNE}sSAzzql8w~JvoNRWd; z2}FVA1Uq+fG92E?$skq1%*Y^O3ARZTbj|GpuyJ6OpsTqUL^R_;UKHKAf`b8kKQF`- zh@74vXj(&pLyZyXsv1xT>ZpN23UpNsDEMq4JcttoIFwT1sU9Q?4P1~Q2a=rNpY9&~9C^B{(oLpTt_IXING zQ4EJ(APo}aU_i(*h^$EDU|?dDSO78{)IVfokb1!Z~_!l=4_0DIu?-YqNIMXAa-ejay4T( zlN}__OX;zp?9K)kR=2EXNCw&3>VoD9+{I2ojlvM>r-t$=d0SQ!Q9 zz&P7k83iBiLONfd z6{3vc9X~i3cK_gHklF;9RQLjw1x+GLZDwI%PrCntl{E{OUT zs4Q6hZWa~>k(0vU1Up?5Jx%S>L`txyAsk4o3veJeHW)$r!Lbf%fp8!-Lb1=NP z&cYzI60~aZ2@@j2%43KOTkvViO~y zlGz$a=LS;FFha^1kRT%iLQe3L11E#7BPY20XAs%s$jQLOBvA~q>1hiS0~5@qX*!Gy zOt5NvIfMhTiHS)m48d${YOr->|GaGHRm%W4P*ViO04 z(livCpxFZ?$iaY+6O?t~WQcHq`Kikl)PIuL2(oDtkju+u|I5&jSk#3n{YrE4fQK~n@skdXl) zCur!-$&lj?v+1KdI7PIAY(hy9D?nEo!cxRe2nS*l6O+<66q}$a0wl=9fRGd9_uypk z@_^ZN!2_Hk-h*sHNfAQ&NGU=Y!hzVt!lER$7CDteQv^tmg#jTa_|AiqLDLgv({FE3 z*GwW(A3a6X=p&_wUI+(b69hT4qh{GV8P*TJ* zh~bbF@g2f}*d)NA6oz6GG(~^}IT#Rff~$Qw8J_tHv|1XJ1}53h-V6-PvJHj| z3@oyLy%`u-8AQHEf!n8`8@eZh+vf}-d%!{x^9>NKat2Y5VfP^lq!>llJ!4^b2IVn{ zf{gkJOt=859M&jXVy-VGt=f2lBhOwx7#MU8aDW$O!o)x$ zX*vgxR%d}0=P`yKhnesaMK!1wr}GL)^)j%1%I1)koz7yEwOk+<>ntYam>O2}0|0dv z5A-QDpb0r}V1Wjj8N<^|K-UQ%-H-xWNvN}345jf362mx63nU1+!HyOAf-{gXiTXi} z7O;~TAje*H!h-(^iphGQ=zfCgGLRUC%RpX)x(xl~I-MtCYy*Ghl|a&BXoB9($RHSZ zo`Ye^c@730HAY4T5rt?D1_2g{UK8Zp&Kk|ZaMlD|I!iH%az}G8e1r0sMERpR7(`4V za?GN$SQ!}9Av^{VkTwApa4P|l%|*jG8DIsLxFD3t!yp1O5tl*+5s(civJA=~YXn$8 z2TFh=&H|imn!Z8qJk{UH$iNi32R^p7lAWP2&a~6iu=PV4$N0}JKKLkTM z1eV|lIMA^b9Py@%3>QLK8I+If@&9f3c9Di3Rn3Hqu^N$P6jzmP6p*BCPuDhk69Rg zJ!WB0KFG+Ge}EHwDyi}sM!_kkIKgM4Drw4O&?Y7Gq#w0G0Hh z&7vS0ECwoV8N(wj7#Rfqo#y~=$q|3mVM&4^WoRgJxs(AMvn)Yylev zTAR$kz@Yz75XDmXZalc_=7C)YI)?##j!i-sE9N;iASZ&Hidf7Ex=4aC{KscbhO94~ z4EoZHJok1pFkAp>)@L;1i3ZK`gtIb;1TqSOjhz7EEau@raSW&|1RKl1AP{zugCX0O zfkA&SVjKZ<$}eMh-WN`Yj4>lSC~#oj15Ik`n=%?QaGku!!2r6tUjHm3+*=@7#&FQm zc9bRUphOE^($3{}iG$($0yYNyuZ&!)VeI#e!rYgkLC?sDa0zJRFJt(_FOVdz@5_jC z$D$RJ#I85&-8jEsW6FLN*)+|0zFFUW`pJoEX02^ql04&HsyYWF=2G~I13KIm);_0Jr9e{^30|UGi1g+6!47dLZ4a8zb;o>XM zus(ql)}Vl644?g#6Osek7^6V;XL0sA%5v( zWN)@>-vB13|xwkprCPz@iXKKn4pZUFBc^9a5(M49UFt;9Rlu8`P~o8F?;SF)~a5 zIpP;12TGiRQ}Ihi0ZhSH*aY9934+e3)&Iz-&cGmS#tn6T8Y81{I}bDfl9BF6{s(qe zgaxGirC*9tc!E}a>6fCmzd&NRSAwCmzd&p0^-EFPU!biY=nKI>i^DJ$f`QtVR9OfH z&%o=!UVY|_*8T#G9_qKCo@NRX!)WDz1fi`Q{6K0h3AQt_gg*oW@L1GyCL4sKHgE|E0`axnC`VsC&L_eha z@z0r&0cAiRq+dUl5j9Xif_MzYxYZkMz`%DsKy5H^LV);H)Dl|KHZWp(xq%UN-jZZ5 zgA5pSUe1Skc@7pY&!N=IAOi-Gmo>qgs7}JXya9`sH<0LMB;0MOc93>Sa| zG4duz5K>?;2)vpOZTGY=3MkKlvfncb*DrvwV;LC*->-(87^EKy?iVhog7yoU61f-{ zl*>SiF&Pfz zy42W?poys|GIFr)Ko>m1&3zY147Bb|Z8d1ECaWRR>8(`^3=Bq-I8g3D1zn13WC!X( ziwlH9df$!Ub_hr#SfvM47}T!?s|2-l!LEd~&0?Ulo<<57Z8IYU)Q&BQZ8IYU)Q&Cv z+h%aDa)GPJtuU{y#q{c0%DlREpuNf<(g|4!Q6$350GS>YRrO(J08a=Ah;nK(K$s$8 z4A2At3LM68{$HF7PQN%AjGlukaB#W+RVj?&p1(L5KpDg63%oxBN|B7=-M=^)K$aMB zF~VgaX6^aK$pEs`NC92u?JrISh2NYEMrMo*BGTFn3voK5w2Mwu%hy9g6tJnp&%${;E z6h7r(&=zK75Rr=jk9(hNWMI&3U;=GP=6ZORgF*Hj2ZMGJqsXDF;Q0fP$!|fr8AOk= zFferg1nr;|-3HUdDRAy82LsPF4hHRSj9d-ZI2aCs*#8+>5~3lQcR4tr&iTX1(EEgg zLA!%v{ZB@QfLK-r?M^Ya3;v7@3!n?c1B zRSxRMYfl#dms@wCVxSQR?HLfUuTU{?D{3Z0Og#qTCa_qK2#2)?Bf|rb=@S$=`Z^dH z1maj3v?Vy$-}^H%ghJJVI_)g!EP~MO?DL_*pgyDaM2NAQp<-ZTCqcxXLB&A5V(lrQ z4mv1Bfm0*sf^Np}n}0YN1fOv*Xzyd)gLH}XRnVm!`^DH8k*?=B553d@bKlQFe#|R7 z;2{OJ40Ki`$jH+o;E;left`9-gcTG5FfmXGuYFvR6%-OEV$cwQiJ^rEOc-qB35f4u zV&G6ZD}w3p3&c1a)UDUPjK#!@pzstIh=s(aC%Ae5&0~OfP$@!%L7ik+|3w)p23k1_ z?>NH*L7O9B9cMMDLQrHOc2HSBMZpgG=?)G_H>eoci$B5VPl1=+F@Z7z*Xe7J?H$@b z7zK|%hqCJ!83jFGLOJ?OjDnA0oXt#(f+}yIGTWFL8CW(%Ks>hr+&nV*!^v>4gONd7 zfpgCp28IKXtPI+U{NN)FK7iPUg5V<#45AvU{APGX2#Iu2RRR0ApK!Y>`SV3nafOb~1LGu{cRM^gHWI@PQXa?{x2tH87pl%WP z$OX_b2*`p`kYf-yW-%}_EQn%d&^A!u>1AMKxB&79=-`G121bTUP#TTPCq z^Z)~cK{P7^s|F7TPlyO3LqaqwgP9Ja1h2aYBg2GfRtB&Y;O%nwYdKox@8jNpAL zpbb0>G0=S~pbb0*G4OpVpbb1bpc+8sFnFH|Xaf(jAY}6icuRvUG%7%O1-7#eCI-rB z@SSxDF^CO36F~OCwlo|7ah7m%FhKUjB|r@{K-zu-69cuP!43j#zbSw!1XZkHZ-KVo ze1LfiwEadQ7Va(3_M3L72GD34*c8zA8)QLkT{&%nS0su8&+KY`}^PDVj(SW)4}$jHD1s;arloD|q$4&xFzCb;_`ORzp@JyaY8{0Nf-)`qTCEdMK~Q=}`0N~16zmDu zwOVhWf?!8MuGJF$oyG}iX4QbEU^AA27Y<^JI}W7AnPAEbXoRg|WK>j$htvV$;0R0p z%gF#rJlazj*&hF7WH5osegL%=rZS4L2?sDTI6wuTKwBU@IzfyK7eK)}jST9qAU`-lO-&k9p2~^53h!jKO0pS4g0H^~X*06>!GVuKU$;fa3Wd3F* z4z|9_6hch@C>Nhfg^)QHBX(TG4h#rJB=()iC1rHBK21agSn^znRpwdJ;o|#dQ z?=>_n?PW%UCKEU<3H^gQ?Kcxob_gTG2as?7Fmdp3{bFQLNML2qPGA;eQ$uRfg2whh zB>-DR2x5*1R4QsGGeb%OSa^fVR_%Sv91Lur{0$QYwfVGDnNb_7U_odyf++;mXGr-S zCJatS>CB*ZBw|L@2kaT|f1C`U@={x#nMeE=BSQel{R+%tY?esw2Oal@;eK#_S4VR{ zIKPA3kCKYO8N`Sg^%_0U%&@j8Go*NfxfHBYlUW4C`=If9ZCy0)g9VB6J}6YQ^}*f; znGd!L(k%f+A2`|?K;A}=wsX)Nff8+?suCmGzy&zS+bHe^tuMofHn1RZ?na6>P@f*` zZZB{#-SiKdA+|E}sQhAN*Z^|=Hf9dC8%P=29_oCQr~q9LpuLkB)HY!d6yf4z@aN)W z(AHsO6pZKMWS9x!gfcM-uHfQicnIQL1P#1#aWZIdb24b(11-uu2HusS4cZq5-Y(9d z-2&=%fqKoLZ2=`53_5xd91MMpObpE189gF67?>vWb24x(;OAtx&CkieY{JMTna#-% zk_}7-nVP9kR!MPox=3FNy17{b6t=q-PkO5`)cX2YDg0LZL zSp=8`AY+lt9gG5-1UMP~3vePhk|uD}b^)7zHE?5pGZ~f{Sw{H?qUc zY%Ye23p_@!xh5|}*t?(n@Bvg;8K(JzQKMtpU!452AtkQHhvhjVK@a5(pb z2M*_oc;awwf+r5=uJy#>-0z+^oa^F+!@2!l7|w+R$!#wT=R)E`-5bNXpawT%c(FGQ z=N|CJ;an~s9L^2*!QtFRJ~*8F&IgBct$cAfx6K#BxsW)&?2F-CP?gOXF6W2CxjB9~ zoV&vhhjUr|aX2@~AH%tz_6}qCe18n*LgM4CKZYF;35x)j9iT=sxDtd&lvgt{z%u}- z24)Oz4!~jhd0d7xV;UkI2r~q3AtWvJBddi3at2&2noG9ACE!htZgAB-JBg8j8+7y= z*e9SWnt_3Vxl9r<>kU!&E}4)z&=rA<;U6N1Q>PG#L!H3$CV25DT`&pK@c85ugDVjykI)1^49>lhg(d|+i@o+ZLz)5gef0L1<$ z!u?j9iQzR=EhwKcUxbK#Ll(Ov!VNldSZ^}OGzJDxa%H}&3)X826$7>9nD0W=8Bak{ z_Z*_m3MvLx_gsX#T#1pv11bjUCNaO&Vqjps5y8li2sP0k6#mmhSWksBGNeGoKpkA> zP62slQG^)S)eLM0l~9F*CNK%GZ$%Rn-V2)24hDvKP%*GN zdkIznQAQLoQ3DRPdGe@YLRP%8V0R;H7TUoBT5`w0z?vM-$glxwtQ{zXW{9wwMWBg6 zLkOl0DTI)P;30%8h!jG*q1J#}*02yd0u=-M(n*~glzNXt#gIej7E}!Eb7%;?go=Un zLPO{tR1B;R8bZioun&6v4>w z0h9-zVFgyjzyQ8miW!ockcHr3g)9gSt4G3+6$0-9V1|YjvKTC^kj3C(g(3(ED-Lr} zMh1nCtPIRy!aQ1{j0^!ESs6gbkn_rlGBPxPIN-5P1_pub7R(^61{a(0h#>U?B}0&L zv=35n@HvWbg9>Aq&%k1XAxIy9!T=hi0-unA)B(hS2C4W2XyFH`RX8FU891(mGcpu> zVr77pa5JGwkn%6G5IjvH3nHb-B~b0iX>u!63>;XFBH$8k2UH9>|DJ}5A*adfP%&_N zhNj8$P%*GNXqrS8gQZDiF?gCp5kyLp3qWB24ZsVaFawRhgVH3!XI2K#H3Oh17gv}H zatH$hBmhM~0r)7Kk-^|IA^gY? z2buywHi80@p$?ppcOx=#6sT+j89{kQ{)1Ewg2qZf345ak!R+v6z0 zy@!JGFbAnT1epx0R$x&8@)Y&jirp_$3Du$d6-$KQ} z>Y&XVWHDIt23ZW=yg?B}YThgWg#k1GFMz@fmYo@XB9(^@(?AY^wGU5#0#GP|k-^|6 zA^<(1N{|8&SqL70$bv`#7y{Lf9DvDCF<1bC%EL6M7;*qsLdC!V0}a4-s2IrIu#8*_ z6$7h-1|YH+EC7+k-~osth!lVcpfG?2-~>>Z!2<9AhyzW5AREEONG-TVHVlC`Y`Q?@ zA;<{IGqM<#@=zcOF&PUBMq=BCl}P0wST9Z6hrf`@!(T`lxdFsM4@O8cyB`sZ(?I1R z$O!5NBP=yy3r1qgL%Igz15g-1Q=`Cdq+oOaaiGB{egT>h`oO7?C5n-OLk?6P{zhcy zTTmrP*%?^~o}G~eq1hRf4xd7`BM0CYs2DgcLCeGMP%-4}%mupCh5_3Da1!7K1)wBU z46GNL4*8&Bpv(oUmyyL_*%?_3o}Ezyk+SmwP#8c1@B%2zVA+}B4^nxU0JRztfHI%} z)B=@ ztQQ)9^Ppm2bC=Q8q3 z!8XiN5Hi^J7pcJd3(09bpn;VJ5C?Sj^>qn^t)O8K@Y&aKpiTZzM}r!zLZGv+gU|#a zt8~Gt;Gr`CoL9X=;WfuPP>~061i7IDnrH&fN%hKN9;kf(_4!72ri>4`QI9`He%eosr=H0~-S~bPf>a zQE>5d5i$peA_koUgoz=~0m8(M^K$sX<9dr&5CI(gqodbl4fyx!|9H2m44<~dL z1>(q)P_XkIVqwnLjjBPEQ{dFhzzXsa%rrwrhQa4E&}IR!10lzmT!;Cl39*n7w6csb z+#(L@&n89`kAlMz=^PEnp^)u!3gSe?x^pT_;cu1I?Zb&H(l6P-o92pkm;maj5&Op<-a; zpgG40DhAFaQ1=BOi-Bg(;X|kz;J|zi3rqzp*&G~j0~(m%oWQ`q0J-GjGbk`YYd&DY zpf$0~4B)_o34u17f&vq?WC12N;Kh}A12h^Sf&Rai5#s3CP+`!{1DK;hbgDz+h&333T zxB(CG$1$i7H&TfzJqzMKWPhk3i$VPn2NeUm54v6?6)FaHAJiXHpkiQk&=fx#Dh5^u z^~Xx67{VX0WB@5X#Ot8NN5G&=@eiQRhXlGZD9~qDGcr7f3WLiYNT7d!3V|+>fh8XA z*_eSIhAajR^ggHWy*QZ)uKCkvq(q#djn#9$0Rhmg@lE+Wv)0Xd8@ z+$aKWD@Z%o98gb&F?K^GK(Fmuq| z13LMJG5iorHB$8qYOyeet42Z1naBvPNAR1|flv){23Rq~oHqy=yx{{0=YVLqt#ETd zn;{v)w;*Kjh7aUSFbP;&62o6L2pMeQ!@%_*i<3bRw90%x;&MYPW(Eev@a!-yhB;we z49skd>^`6cU|?@DfUnbImSdDe5o8B-spS|gcqZ>+U}ykkc_l_k?m0-SXF$zg{wv%J z?4bG=Y7}JkS_Y#oJ90e&F7YxLMc8K}9cKhu8OdDAD8>$2tcIdJf>97fJ2-RLg6_}e zQt#np2n8(yM)I5r_|D)3v0M!2Vlh1jwi~bKJ`AYmz{50P&*?3S;9xl2$i%?H!^p@9 zO7GxMVUFZr5P2xbz~I0kS{%;~IS|1>bTaJVOcT+|)$9!aCNnZHT8Pe*=VbT;zHLHu zk01ktDIf|zDL_H^>2K~_fLHD@`%TaH;)u`&wlo#S9gKgYqq@{x^ESp7N& zL(Fv!29_mkjDqi;a4;x6jaiuyMWv<=69@Y*aVCb-Q0<^x#6BCe?vjD+p9~YjWvDo4pApM>76rDC zXo6s41(X?4RDz9VmtbPJ4b=|nkwc9Ihmt}$D+9|#XefPv`Vu9SV1fu=fLF)*fx=auOpeH|H!h zMurLHtPE0DI2m~4_b@PA0LgH%D)D51&d02Po{#zW3FofB!^*@9PH)Ot5HEnLIe2`AQUo7aF^PqX;SviM1B)9gyFTd36tElv1K4OURt0dW3l(DkB^nk#R(_rb#taMw zwX6&*F|1-7Z-f~c5^7l)6n~1a#mggxoj`Vi!+IssNH|y!94?$5h71fBK>B_OGhnn^ zSVCDPAjMW4D+5a+G>l*l0s9daMleB;(TFgDiGnujvLr*p2qp;D4+|rhAY4BpjCO*< z=ot$a!(vWO29`WlcF?&VFb$xT%2LeA!wU+50FXP&SVee2-f94GT3O|I?h7+A8~}0J zSY>(UfWo1kl>szO#0zRNIn=W$Ja~aq` z$8x|#L2UuBQqZv+$b!&gIoQ4-U3Uyp%u>cG!u}FXP{@gggMoqn12jE@>}L#5?_gx$ z7s!L;V$eOejNwrojEEz1L6OH8uEEO10BT^doM8oZcEC=6$V9SoF@V}zEN{?dI$60G zK+P`}0XDcEP?gLWzKfNM0n{2}F+#|II-!i=uUWYmK#emNe>PC#6l@PDO)-Xpn#X9y zD6nxcgtKumuynHV2S9xQF$UCRg6oF}ff_md2`H*SJ6QM&P=r7mSi~EkLZH(%7{fuw zjIu?0Ffw#Og+Uu;;pd9N1VMqRohHb@Aie>r%oaRW2Re%nd{X~Ds4%z?7i8xE@4!HgNhy&4XBwgLC~O_ zb{LZ&17d?WM0*k&7sGT=uqCpym4nW~1BWsLICHWjK}~^*f%hP3Ut&Zy#RlxLg=}05 z*FdIJv$M4znF2Z(fTaeTDfdBc5l&O)WSFbW$-tt{&M3S+jg#Sh8Ycsb06U{_S0*RJ zu}n?|mcwj}!bVw~40&0c3@pdl7#WyOuj61~+P0nna*M?#K?Vj!M%fvlP?Mbt;>a!* z1l`uLN)WWJSLCZE1A_&JsQzR&$Qd3CqTfMXgei;+92^WH7c?0d3^)*d1O-GNK?Bi= zPyzQ5>Op!Kz&!^rQ-DF_lqTpz0g3J@$Tz0!)MQ}T3swud>THiD1H%OB=$2OoQP7zew?OyX35dFA zF)%cNL=^-Z)-y0{TF=11Vh%bUU8ARi>7#Mbfm>hy1zHu-pe&=9d z5oBZ(x{w6P_@H8kF+7`@i(v^f7XynpBfG;?Muy8!Sx|qMMS_uoLGZ+H4u*ffIT%=^ zK_{(5a4|3lub2QiUXkT4JEQRY^BfG%su>wr+SnO|^=?9C4ze=}hHZiD9AnwV&ZsdV z5n?SQYuYezF-&6OVqiHby#Ok45uC>MGI23veB)qXIm;3MosprSn3aL$90$i%Q$~gZ zAoeP8-W8^d3<4#r49Z)1X6%U;_2lR2!fTaL`x*V>mwx7eg4R zw%^9Wzyzw&1b1?BGH`HlGO*;cGBWTR3RO0taMbEh&QG9_dvJ2DVj5Mt~-3Sa!2>pqLA? zmE{l)K?VjiGeO3Ypj{{c>fN2--bpRYttZ&QCzR(vWkDnDEGOAHctG8c3!wV)G`kqM z`@ztJ)cr63abVq#1Q3UniI)e|-CfYc$^dQ%f$IE=P*XwGk`TBd1a4Vg0BL}AJANUn zf%T731i>1?-42FkaDN5T?JxjwVBHS!0#F=4mfXOtM-fDrk^s>N>2^#2abVq!10W8p z+hK~$NEAVYkslx$Mc6?-nJTD8Pzw^;?ch1Nmw`c{1(a9V`8hx*=m)g0GFT+Z@_-J~ zZ)jm<03D#u13Ex|1Be4VIA660;w_L-;0p>shd8`rU}SgzGV46MEYCv*Mh1aaXjA4l z10%zAsO_MWM_6mwdDuZsna5B;(AXYpB)c$J@Gn#ll|)>{G-N^OdJlF!Ek=eL#SjxfIU3{u1_p4wssJ?@l-*d)vkUNmZYgkR zWo2Nwz%B{Ct)QTll|dhNTfqVlhlkOU6LdMo1rTQus0YOh%BKu%px!;Z2s@~xVMVfz z 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⪻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

6q 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�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 -keZTel4hK0 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

vIE5j0Wu7Wvee*(yh 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 IfW~D20G~Yz3;+NC diff --git a/tests/ui/auxiliary/macro_use_helper.rs b/tests/ui/auxiliary/macro_use_helper.rs new file mode 100644 index 00000000000..c63149a6819 --- /dev/null +++ b/tests/ui/auxiliary/macro_use_helper.rs @@ -0,0 +1,55 @@ +extern crate macro_rules; + +// STMT +#[macro_export] +macro_rules! pub_macro { + () => { + let _ = "hello Mr. Vonnegut"; + }; +} + +pub mod inner { + pub use super::*; + + // RE-EXPORT + // this will stick in `inner` module + pub use macro_rules::try_err; + + // ITEM + #[macro_export] + macro_rules! inner_mod { + () => { + #[allow(dead_code)] + pub struct Tardis; + }; + } +} + +// EXPR +#[macro_export] +macro_rules! function { + () => { + if true { + } else { + } + }; +} + +// TYPE +#[macro_export] +macro_rules! ty_mac { + () => { + Vec + }; +} + +mod extern_exports { + pub(super) mod private_inner { + #[macro_export] + macro_rules! pub_in_private { + ($name:ident) => { + let $name = String::from("secrets and lies"); + }; + } + } +} diff --git a/tests/ui/macro_use_import.rs b/tests/ui/macro_use_import.rs deleted file mode 100644 index 6490a2107d5..00000000000 --- a/tests/ui/macro_use_import.rs +++ /dev/null @@ -1,12 +0,0 @@ -// compile-flags: --edition 2018 -#![warn(clippy::macro_use_import)] - -use std::collections::HashMap; -#[macro_use] -use std::prelude; - -fn main() { - let _ = HashMap::::new(); - serde_if_integer128!(""); - println!(); -} diff --git a/tests/ui/macro_use_import.stderr b/tests/ui/macro_use_import.stderr deleted file mode 100644 index 1d86ba58441..00000000000 --- a/tests/ui/macro_use_import.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_import.rs:5:1 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::` - | - = note: `-D clippy::macro-use-import` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 60c64ee8146..76911b0c565 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -1,11 +1,40 @@ -// edition:2018 +// compile-flags: --edition 2018 +// aux-build:macro_rules.rs +// aux-build:macro_use_helper.rs + +#![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] -use std::collections::HashMap; #[macro_use] -use std::prelude; +extern crate macro_use_helper as mac; + +#[macro_use] +extern crate clippy_mini_macro_test as mini_mac; + +mod a { + #[macro_use] + use std::prelude; + #[macro_use] + use mac; + #[macro_use] + use mini_mac; + #[macro_use] + use mac::inner; + + #[derive(ClippyMiniMacroTest)] + struct Test; + + fn main() { + pub_macro!(); + inner_mod!(); + pub_in_private!(_var); + function!(); + let v: ty_mac!() = Vec::default(); + + inner::try_err!(); + } +} fn main() { - let _ = HashMap::::new(); println!(); } From 8ffbf6f94d9cc77ade0596ee104f3549b8db452d Mon Sep 17 00:00:00 2001 From: Devin R Date: Fri, 13 Mar 2020 18:54:32 -0400 Subject: [PATCH 0190/1110] use hashset not map for keeping track of seen macro refs remove stdout, fix clippy warnings, fmtcar --- clippy_lints/src/macro_use.rs | 131 +++++++++---------------- tests/ui/auxiliary/macro_use_helper.rs | 8 +- tests/ui/macro_use_import.stdout | 0 tests/ui/macro_use_imports.rs | 14 +-- tests/ui/macro_use_imports.stderr | 44 ++++++++- 5 files changed, 97 insertions(+), 100 deletions(-) delete mode 100644 tests/ui/macro_use_import.stdout diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 4c89647a574..9519fa6093b 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -2,7 +2,7 @@ use crate::utils::{in_macro, snippet, span_lint_and_sugg}; use hir::def::{DefKind, Res}; use if_chain::if_chain; use rustc_ast::ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -29,7 +29,7 @@ declare_clippy_lint! { const BRACKETS: &[char] = &['<', '>']; -/// MacroRefData includes the name of the macro +/// `MacroRefData` includes the name of the macro /// and the path from `SourceMap::span_to_filename`. #[derive(Debug, Clone)] pub struct MacroRefData { @@ -38,11 +38,11 @@ pub struct MacroRefData { } impl MacroRefData { - pub fn new(name: String, span: Span, ecx: &LateContext<'_, '_>) -> Self { + pub fn new(name: &str, span: Span, ecx: &LateContext<'_, '_>) -> Self { let mut path = ecx.sess().source_map().span_to_filename(span).to_string(); // std lib paths are <::std::module::file type> - // so remove brackets and space + // so remove brackets, space and type. if path.contains('<') { path = path.replace(BRACKETS, ""); } @@ -57,13 +57,12 @@ impl MacroRefData { } #[derive(Default)] +#[allow(clippy::module_name_repetitions)] pub struct MacroUseImports { /// the actual import path used and the span of the attribute above it. imports: Vec<(String, Span)>, - /// the span of the macro reference and the `MacroRefData` - /// for the use of the macro. - /// TODO make this FxHashSet to guard against inserting already found macros - collected: FxHashMap, + /// the span of the macro reference, kept to ensure only one reference is used per macro call. + collected: FxHashSet, mac_refs: Vec<(Span, MacroRefData)>, } @@ -72,34 +71,28 @@ impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { fn check_item(&mut self, lcx: &LateContext<'_, '_>, item: &hir::Item<'_>) { if_chain! { - if lcx.sess().opts.edition == Edition::Edition2018; - if let hir::ItemKind::Use(path, _kind) = &item.kind; - if let Some(mac_attr) = item - .attrs - .iter() - .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); - if let Res::Def(DefKind::Mod, id) = path.res; - then { - // println!("{:#?}", lcx.tcx.def_path_str(id)); - for kid in lcx.tcx.item_children(id).iter() { - // println!("{:#?}", kid); - if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span.clone(); - - // println!("{:#?}", lcx.tcx.def_path_str(mac_id)); - - self.imports.push((lcx.tcx.def_path_str(mac_id), span)); + if lcx.sess().opts.edition == Edition::Edition2018; + if let hir::ItemKind::Use(path, _kind) = &item.kind; + if let Some(mac_attr) = item + .attrs + .iter() + .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + if let Res::Def(DefKind::Mod, id) = path.res; + then { + for kid in lcx.tcx.item_children(id).iter() { + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span; + self.imports.push((lcx.tcx.def_path_str(mac_id), span)); + } } - } - } else { + } else { if in_macro(item.span) { let call_site = item.span.source_callsite(); let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = item.span.source_callee() { - if !self.collected.contains_key(&call_site) { - let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx); - self.mac_refs.push((call_site, mac.clone())); - self.collected.insert(call_site, mac); + if !self.collected.contains(&call_site) { + self.mac_refs.push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); + self.collected.insert(call_site); } } } @@ -111,18 +104,16 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { let call_site = attr.span.source_callsite(); let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = attr.span.source_callee() { - if !self.collected.contains_key(&call_site) { - println!("{:?}\n{:#?}", call_site, attr); - + if !self.collected.contains(&call_site) { let name = if name.contains("::") { name.split("::").last().unwrap().to_string() } else { name.to_string() }; - let mac = MacroRefData::new(name, callee.def_site, lcx); - self.mac_refs.push((call_site, mac.clone())); - self.collected.insert(call_site, mac); + self.mac_refs + .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); + self.collected.insert(call_site); } } } @@ -132,16 +123,16 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { let call_site = expr.span.source_callsite(); let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = expr.span.source_callee() { - if !self.collected.contains_key(&call_site) { + if !self.collected.contains(&call_site) { let name = if name.contains("::") { name.split("::").last().unwrap().to_string() } else { name.to_string() }; - let mac = MacroRefData::new(name, callee.def_site, lcx); - self.mac_refs.push((call_site, mac.clone())); - self.collected.insert(call_site, mac); + self.mac_refs + .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); + self.collected.insert(call_site); } } } @@ -151,16 +142,16 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { let call_site = stmt.span.source_callsite(); let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = stmt.span.source_callee() { - if !self.collected.contains_key(&call_site) { + if !self.collected.contains(&call_site) { let name = if name.contains("::") { name.split("::").last().unwrap().to_string() } else { name.to_string() }; - let mac = MacroRefData::new(name, callee.def_site, lcx); - self.mac_refs.push((call_site, mac.clone())); - self.collected.insert(call_site, mac); + self.mac_refs + .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); + self.collected.insert(call_site); } } } @@ -170,10 +161,10 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { let call_site = pat.span.source_callsite(); let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = pat.span.source_callee() { - if !self.collected.contains_key(&call_site) { - let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx); - self.mac_refs.push((call_site, mac.clone())); - self.collected.insert(call_site, mac); + if !self.collected.contains(&call_site) { + self.mac_refs + .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); + self.collected.insert(call_site); } } } @@ -183,22 +174,18 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { let call_site = ty.span.source_callsite(); let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = ty.span.source_callee() { - if !self.collected.contains_key(&call_site) { - let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx); - self.mac_refs.push((call_site, mac.clone())); - self.collected.insert(call_site, mac); + if !self.collected.contains(&call_site) { + self.mac_refs + .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); + self.collected.insert(call_site); } } } } fn check_crate_post(&mut self, lcx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { - for (import, span) in self.imports.iter() { - let matched = self - .mac_refs - .iter() - .find(|(_span, mac)| import.ends_with(&mac.name)) - .is_some(); + for (import, span) in &self.imports { + let matched = self.mac_refs.iter().any(|(_span, mac)| import.ends_with(&mac.name)); if matched { self.mac_refs.retain(|(_span, mac)| !import.ends_with(&mac.name)); @@ -218,30 +205,8 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { if !self.mac_refs.is_empty() { // TODO if not empty we found one we could not make a suggestion for // such as std::prelude::v1 or something else I haven't thought of. - // println!("{:#?}", self.mac_refs); + // If we defer the calling of span_lint_and_sugg we can make a decision about its + // applicability? } } } - -const PRELUDE: &[&str] = &[ - "marker", "ops", "convert", "iter", "option", "result", "borrow", "boxed", "string", "vec", "macros", -]; - -/// This is somewhat of a fallback for imports from `std::prelude` because they -/// are not recognized by `LateLintPass::check_item` `lcx.tcx.item_children(id)` -fn make_path(mac: &MacroRefData, use_path: &str) -> String { - let segs = mac.path.split("::").filter(|s| *s != "").collect::>(); - - if segs.starts_with(&["std"]) && PRELUDE.iter().any(|m| segs.contains(m)) { - return format!( - "std::prelude::{} is imported by default, remove `use` statement", - mac.name - ); - } - - if use_path.split("::").count() == 1 { - return format!("{}::{}", use_path, mac.name); - } - - mac.path.clone() -} diff --git a/tests/ui/auxiliary/macro_use_helper.rs b/tests/ui/auxiliary/macro_use_helper.rs index c63149a6819..7cc4e1d736a 100644 --- a/tests/ui/auxiliary/macro_use_helper.rs +++ b/tests/ui/auxiliary/macro_use_helper.rs @@ -17,7 +17,7 @@ pub mod inner { // ITEM #[macro_export] - macro_rules! inner_mod { + macro_rules! inner_mod_macro { () => { #[allow(dead_code)] pub struct Tardis; @@ -27,7 +27,7 @@ pub mod inner { // EXPR #[macro_export] -macro_rules! function { +macro_rules! function_macro { () => { if true { } else { @@ -37,7 +37,7 @@ macro_rules! function { // TYPE #[macro_export] -macro_rules! ty_mac { +macro_rules! ty_macro { () => { Vec }; @@ -46,7 +46,7 @@ macro_rules! ty_mac { mod extern_exports { pub(super) mod private_inner { #[macro_export] - macro_rules! pub_in_private { + macro_rules! pub_in_private_macro { ($name:ident) => { let $name = String::from("secrets and lies"); }; diff --git a/tests/ui/macro_use_import.stdout b/tests/ui/macro_use_import.stdout deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 76911b0c565..bc8762df593 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -12,8 +12,6 @@ extern crate macro_use_helper as mac; extern crate clippy_mini_macro_test as mini_mac; mod a { - #[macro_use] - use std::prelude; #[macro_use] use mac; #[macro_use] @@ -26,15 +24,13 @@ mod a { fn main() { pub_macro!(); - inner_mod!(); - pub_in_private!(_var); - function!(); - let v: ty_mac!() = Vec::default(); + inner_mod_macro!(); + pub_in_private_macro!(_var); + function_macro!(); + let v: ty_macro!() = Vec::default(); inner::try_err!(); } } -fn main() { - println!(); -} +fn main() {} diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index b5e3dbec572..6bcacd0be19 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,10 +1,46 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:5:1 + --> $DIR/macro_use_imports.rs:15:5 | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::` +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::pub_macro` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` -error: aborting due to previous error +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:15:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner_mod_macro` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:15:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::function_macro` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:15:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::ty_macro` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:15:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::pub_in_private_macro` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:17:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:19:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::try_err` + +error: aborting due to 7 previous errors From 1d9e80ad7b5b69caa8b9f9c44d47f26c4bbc4c7b Mon Sep 17 00:00:00 2001 From: Devin R Date: Mon, 23 Mar 2020 22:13:35 -0400 Subject: [PATCH 0191/1110] remove session --- clippy_lints/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 89f55986f63..991899f89bc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -60,7 +60,6 @@ extern crate rustc_trait_selection; #[allow(unused_extern_crates)] extern crate rustc_typeck; -use rustc::session::Session; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; From 8bc106b29d3d2ee382f6dc4a2e9d6eecf534c6ed Mon Sep 17 00:00:00 2001 From: Devin R Date: Sun, 10 May 2020 09:06:33 -0400 Subject: [PATCH 0192/1110] fix some of the review, rename, fmt, refactor --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/macro_use.rs | 130 +++++++++++++++------------------- tests/ui/macro_use_imports.rs | 2 +- 3 files changed, 60 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 991899f89bc..021fbe932d8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1060,7 +1060,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_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 9519fa6093b..9c8035f54a9 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -38,8 +38,8 @@ pub struct MacroRefData { } impl MacroRefData { - pub fn new(name: &str, span: Span, ecx: &LateContext<'_, '_>) -> Self { - let mut path = ecx.sess().source_map().span_to_filename(span).to_string(); + pub fn new(name: &str, callee: Span, cx: &LateContext<'_, '_>) -> Self { + let mut path = cx.sess().source_map().span_to_filename(callee).to_string(); // std lib paths are <::std::module::file type> // so remove brackets, space and type. @@ -63,15 +63,37 @@ pub struct MacroUseImports { imports: Vec<(String, Span)>, /// the span of the macro reference, kept to ensure only one reference is used per macro call. collected: FxHashSet, - mac_refs: Vec<(Span, MacroRefData)>, + mac_refs: Vec, } impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); +impl MacroUseImports { + fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) { + if !self.collected.contains(&call_site) { + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + self.mac_refs.push(MacroRefData::new(&name, callee, cx)); + self.collected.insert(call_site); + } + } + + fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) { + if !self.collected.contains(&call_site) { + self.mac_refs.push(MacroRefData::new(&name, callee, cx)); + self.collected.insert(call_site); + } + } +} + impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { - fn check_item(&mut self, lcx: &LateContext<'_, '_>, item: &hir::Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) { if_chain! { - if lcx.sess().opts.edition == Edition::Edition2018; + if cx.sess().opts.edition == Edition::Edition2018; if let hir::ItemKind::Use(path, _kind) = &item.kind; if let Some(mac_attr) = item .attrs @@ -79,126 +101,88 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); if let Res::Def(DefKind::Mod, id) = path.res; then { - for kid in lcx.tcx.item_children(id).iter() { + for kid in cx.tcx.item_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { let span = mac_attr.span; - self.imports.push((lcx.tcx.def_path_str(mac_id), span)); + self.imports.push((cx.tcx.def_path_str(mac_id), span)); } } } else { - if in_macro(item.span) { - let call_site = item.span.source_callsite(); - let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = item.span.source_callee() { - if !self.collected.contains(&call_site) { - self.mac_refs.push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); - self.collected.insert(call_site); + if in_macro(item.span) { + let call_site = item.span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = item.span.source_callee() { + if !self.collected.contains(&call_site) { + self.mac_refs.push(MacroRefData::new(&name, callee.def_site, cx)); + self.collected.insert(call_site); + } } } - } } } } - fn check_attribute(&mut self, lcx: &LateContext<'_, '_>, attr: &ast::Attribute) { + fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) { if in_macro(attr.span) { let call_site = attr.span.source_callsite(); - let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = attr.span.source_callee() { - if !self.collected.contains(&call_site) { - let name = if name.contains("::") { - name.split("::").last().unwrap().to_string() - } else { - name.to_string() - }; - - self.mac_refs - .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); - self.collected.insert(call_site); - } + self.push_unique_macro(cx, &name, call_site, callee.def_site); } } } - fn check_expr(&mut self, lcx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { if in_macro(expr.span) { let call_site = expr.span.source_callsite(); - let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = expr.span.source_callee() { - if !self.collected.contains(&call_site) { - let name = if name.contains("::") { - name.split("::").last().unwrap().to_string() - } else { - name.to_string() - }; - - self.mac_refs - .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); - self.collected.insert(call_site); - } + self.push_unique_macro(cx, &name, call_site, callee.def_site); } } } - fn check_stmt(&mut self, lcx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { + fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { if in_macro(stmt.span) { let call_site = stmt.span.source_callsite(); - let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = stmt.span.source_callee() { - if !self.collected.contains(&call_site) { - let name = if name.contains("::") { - name.split("::").last().unwrap().to_string() - } else { - name.to_string() - }; - - self.mac_refs - .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); - self.collected.insert(call_site); - } + self.push_unique_macro(cx, &name, call_site, callee.def_site); } } } - fn check_pat(&mut self, lcx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) { + fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) { if in_macro(pat.span) { let call_site = pat.span.source_callsite(); - let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = pat.span.source_callee() { - if !self.collected.contains(&call_site) { - self.mac_refs - .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); - self.collected.insert(call_site); - } + self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site); } } } - fn check_ty(&mut self, lcx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { + fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { if in_macro(ty.span) { let call_site = ty.span.source_callsite(); - let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_"); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = ty.span.source_callee() { - if !self.collected.contains(&call_site) { - self.mac_refs - .push((call_site, MacroRefData::new(&name, callee.def_site, lcx))); - self.collected.insert(call_site); - } + self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site); } } } - fn check_crate_post(&mut self, lcx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { + fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { for (import, span) in &self.imports { - let matched = self.mac_refs.iter().any(|(_span, mac)| import.ends_with(&mac.name)); + let matched = self.mac_refs.iter().any(|mac| import.ends_with(&mac.name)); if matched { - self.mac_refs.retain(|(_span, mac)| !import.ends_with(&mac.name)); + self.mac_refs.retain(|mac| !import.ends_with(&mac.name)); let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition"; let help = format!("use {}", import); span_lint_and_sugg( - lcx, + cx, MACRO_USE_IMPORTS, *span, msg, "remove the attribute and import the macro directly, try", help, - Applicability::HasPlaceholders, + Applicability::MaybeIncorrect, ) } } diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index bc8762df593..2d4f71e5d53 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -22,7 +22,7 @@ mod a { #[derive(ClippyMiniMacroTest)] struct Test; - fn main() { + fn test() { pub_macro!(); inner_mod_macro!(); pub_in_private_macro!(_var); From 451363dc59b3030fee82e4faf04684c068f619cc Mon Sep 17 00:00:00 2001 From: Devin R Date: Thu, 14 May 2020 18:20:07 -0400 Subject: [PATCH 0193/1110] still working on displaying nested imports --- clippy_lints/src/macro_use.rs | 291 +++++++++++++++++++++++++--------- 1 file changed, 214 insertions(+), 77 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 9c8035f54a9..8dddd6d716d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -2,7 +2,7 @@ use crate::utils::{in_macro, snippet, span_lint_and_sugg}; use hir::def::{DefKind, Res}; use if_chain::if_chain; use rustc_ast::ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -38,7 +38,7 @@ pub struct MacroRefData { } impl MacroRefData { - pub fn new(name: &str, callee: Span, cx: &LateContext<'_, '_>) -> Self { + pub fn new(name: String, callee: Span, cx: &LateContext<'_, '_>) -> Self { let mut path = cx.sess().source_map().span_to_filename(callee).to_string(); // std lib paths are <::std::module::file type> @@ -50,7 +50,7 @@ impl MacroRefData { path = path.split(' ').next().unwrap().to_string(); } Self { - name: name.to_string(), + name, path, } } @@ -69,23 +69,31 @@ pub struct MacroUseImports { impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); impl MacroUseImports { - fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) { - if !self.collected.contains(&call_site) { - let name = if name.contains("::") { - name.split("::").last().unwrap().to_string() - } else { - name.to_string() - }; - - self.mac_refs.push(MacroRefData::new(&name, callee, cx)); - self.collected.insert(call_site); + fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, span: Span) { + let call_site = span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = span.source_callee() { + if !self.collected.contains(&call_site) { + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx)); + self.collected.insert(call_site); + } } } - fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) { - if !self.collected.contains(&call_site) { - self.mac_refs.push(MacroRefData::new(&name, callee, cx)); - self.collected.insert(call_site); + fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, span: Span) { + let call_site = span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = span.source_callee() { + if !self.collected.contains(&call_site) { + self.mac_refs.push(MacroRefData::new(name.to_string(), callee.def_site, cx)); + self.collected.insert(call_site); + } } } } @@ -93,104 +101,233 @@ impl MacroUseImports { impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) { if_chain! { - if cx.sess().opts.edition == Edition::Edition2018; - if let hir::ItemKind::Use(path, _kind) = &item.kind; - if let Some(mac_attr) = item - .attrs - .iter() - .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); - if let Res::Def(DefKind::Mod, id) = path.res; - then { - for kid in cx.tcx.item_children(id).iter() { - if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span; - self.imports.push((cx.tcx.def_path_str(mac_id), span)); - } - } - } else { - if in_macro(item.span) { - let call_site = item.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = item.span.source_callee() { - if !self.collected.contains(&call_site) { - self.mac_refs.push(MacroRefData::new(&name, callee.def_site, cx)); - self.collected.insert(call_site); - } - } + if cx.sess().opts.edition == Edition::Edition2018; + if let hir::ItemKind::Use(path, _kind) = &item.kind; + if let Some(mac_attr) = item + .attrs + .iter() + .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + if let Res::Def(DefKind::Mod, id) = path.res; + then { + for kid in cx.tcx.item_children(id).iter() { + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span; + self.imports.push((cx.tcx.def_path_str(mac_id), span)); } + } + } else { + if in_macro(item.span) { + self.push_unique_macro_pat_ty(cx, item.span); + } } } } fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) { if in_macro(attr.span) { - let call_site = attr.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = attr.span.source_callee() { - self.push_unique_macro(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro(cx, attr.span); } } fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { if in_macro(expr.span) { - let call_site = expr.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = expr.span.source_callee() { - self.push_unique_macro(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro(cx, expr.span); } } fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { if in_macro(stmt.span) { - let call_site = stmt.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = stmt.span.source_callee() { - self.push_unique_macro(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro(cx, stmt.span); } } fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) { if in_macro(pat.span) { - let call_site = pat.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = pat.span.source_callee() { - self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro_pat_ty(cx, pat.span); } } fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { if in_macro(ty.span) { - let call_site = ty.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = ty.span.source_callee() { - self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro_pat_ty(cx, ty.span); } } fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { + let mut import_map = FxHashMap::default(); for (import, span) in &self.imports { - let matched = self.mac_refs.iter().any(|mac| import.ends_with(&mac.name)); + let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name)); + + if let Some(idx) = found_idx { + let _ = self.mac_refs.remove(idx); + proccess_macro_path(*span, import, &mut import_map); + } + } + println!("{:#?}", import_map); + let mut imports = vec![]; + for (root, rest) in import_map { + let mut path = format!("use {}::", root); + let mut s = None; + let mut count = 1; + let rest_len = rest.len(); + if rest_len > 1 { + path.push_str("{"); + } + for m in &rest { + println!("{} => {:?}", root, m); + if count == 1 { + s = Some(m.span()); + } + + let comma = if rest_len == count { "" } else { ", " }; + match m { + ModPath::Item { item, .. } => { + path.push_str(&format!("{}{}", item, comma)); + } + ModPath::Nested { names, item, span } => { + let nested = rest.iter() + // filter "self" out + .filter(|other_m| other_m != &m) + // this matches the first path segment and filters non ModPath::Nested items + .filter(|other_m| other_m.matches(0, m)) + .collect::>(); - if matched { - self.mac_refs.retain(|mac| !import.ends_with(&mac.name)); - let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition"; + println!("{:#?}", nested); + + if nested.is_empty() { + path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma)) + } else { + // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2} + let mod_path = if names.len() - 1 > 0 { + ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, } + } else { + ModPath::Item { item: names[0].to_string(), span: *span, } + }; + let names = recursive_path_push(mod_path, comma, &rest, String::new()); + path.push_str(&format!("{}::{{{}}}{}", names, item, comma)) + } + } + } + count += 1; + } + if rest_len > 1 { + path.push_str("};"); + } + if let Some(span) = s { + imports.push((span, path)) + } + } + + if !self.mac_refs.is_empty() { + // TODO if not empty we found one we could not make a suggestion for + // such as std::prelude::v1 or something else I haven't thought of. + // If we defer the calling of span_lint_and_sugg we can make a decision about its + // applicability? + } else { + for (span, import) in imports { let help = format!("use {}", import); span_lint_and_sugg( cx, MACRO_USE_IMPORTS, - *span, - msg, + span, + "`macro_use` attributes are no longer needed in the Rust 2018 edition", "remove the attribute and import the macro directly, try", help, Applicability::MaybeIncorrect, ) } } - if !self.mac_refs.is_empty() { - // TODO if not empty we found one we could not make a suggestion for - // such as std::prelude::v1 or something else I haven't thought of. - // If we defer the calling of span_lint_and_sugg we can make a decision about its - // applicability? + } +} + +#[derive(Debug, PartialEq)] +enum ModPath { + Item { item: String, span: Span, }, + Nested { names: Vec, item: String, span: Span, }, +} + +impl ModPath { + fn span(&self) -> Span { + match self { + Self::Item { span, .. } => *span, + Self::Nested { span, .. } => *span, + } + } + + fn item(&self) -> &str { + match self { + Self::Item { item, .. } => item, + Self::Nested { item, .. } => item, + } + } + + fn matches(&self, idx: usize, other: &ModPath) -> bool { + match (self, other) { + (Self::Item { item, .. }, Self::Item { item: other_item, .. }) => item == other_item, + (Self::Nested { names, .. }, Self::Nested { names: other_names, .. }) => { + match (names.get(idx), other_names.get(idx)) { + (Some(seg), Some(other_seg)) => seg == other_seg, + (_, _) => false, + } + } + (_, _) => false, } } } + +fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap>) { + let mut mod_path = import.split("::").collect::>(); + + if mod_path.len() == 2 { + let item_list = import_map.entry(mod_path[0].to_string()) + .or_insert(vec![]); + + if !item_list.iter().any(|mods| mods.item() == mod_path[1]) { + item_list.push(ModPath::Item{ + item: mod_path[1].to_string(), + span, + }); + } + } else if mod_path.len() > 2 { + let first = mod_path.remove(0); + let name = mod_path.remove(mod_path.len() - 1); + + let nested = ModPath::Nested { + names: mod_path.into_iter().map(ToString::to_string).collect(), + item: name.to_string(), + span, + }; + import_map.entry(first.to_string()) + .or_insert(vec![]) + .push(nested); + } else { + unreachable!("test to see if code path hit TODO REMOVE") + } +} + +fn recursive_path_push(module: ModPath, comma: &str, rest: &[ModPath], mut path: String) -> String { + match &module { + ModPath::Item { item, .. } => { + path.push_str(&format!("{}{}", item, comma)); + } + ModPath::Nested { names, item, span } => { + let nested = rest.iter() + // filter "self" out + .filter(|other_m| other_m != &&module) + // this matches the first path segment and filters non ModPath::Nested items + .filter(|other_m| other_m.matches(0, &module)) + .collect::>(); + + println!("{:#?}", nested); + + if nested.is_empty() { + path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma)) + } else { + // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2} + let mod_path = if names.len() - 1 > 0 { + ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, } + } else { + ModPath::Item { item: names[0].to_string(), span: *span, } + }; + let names = recursive_path_push(mod_path, comma, rest, path.to_string()); + // path.push_str(&format!("{}{}", item, comma)); + } + } + } + path +} From d4f60b5ff42a4e8b5889879664002f90dacd6c04 Mon Sep 17 00:00:00 2001 From: Devin R Date: Fri, 15 May 2020 08:36:56 -0400 Subject: [PATCH 0194/1110] wip: of handling nested import paths for multi-macro paths --- clippy_lints/src/macro_use.rs | 197 ++++++++++++++----------- tests/ui/auxiliary/macro_use_helper.rs | 5 + tests/ui/macro_use_imports.rs | 4 + tests/ui/macro_use_imports.stderr | 38 +---- 4 files changed, 122 insertions(+), 122 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 8dddd6d716d..1e1f27e9430 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -49,10 +49,7 @@ impl MacroRefData { if path.contains(' ') { path = path.split(' ').next().unwrap().to_string(); } - Self { - name, - path, - } + Self { name, path } } } @@ -79,7 +76,7 @@ impl MacroUseImports { } else { name.to_string() }; - + self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx)); self.collected.insert(call_site); } @@ -91,7 +88,8 @@ impl MacroUseImports { let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = span.source_callee() { if !self.collected.contains(&call_site) { - self.mac_refs.push(MacroRefData::new(name.to_string(), callee.def_site, cx)); + self.mac_refs + .push(MacroRefData::new(name.to_string(), callee.def_site, cx)); self.collected.insert(call_site); } } @@ -147,78 +145,123 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { self.push_unique_macro_pat_ty(cx, ty.span); } } - + #[allow(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { let mut import_map = FxHashMap::default(); for (import, span) in &self.imports { let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name)); - + if let Some(idx) = found_idx { let _ = self.mac_refs.remove(idx); proccess_macro_path(*span, import, &mut import_map); } } - println!("{:#?}", import_map); + // println!("{:#?}", import_map); let mut imports = vec![]; for (root, rest) in import_map { let mut path = format!("use {}::", root); - let mut s = None; + let mut attr_span = None; + // when a multiple nested paths are found one may be written to the string + // before it is found in this loop so we make note and skip it when this + // loop finds it + let mut found_nested = vec![]; let mut count = 1; let rest_len = rest.len(); + if rest_len > 1 { path.push_str("{"); } + for m in &rest { - println!("{} => {:?}", root, m); - if count == 1 { - s = Some(m.span()); + if attr_span.is_none() { + attr_span = Some(m.span()); + } + if found_nested.contains(&m) { + continue; } - let comma = if rest_len == count { "" } else { ", " }; match m { ModPath::Item { item, .. } => { path.push_str(&format!("{}{}", item, comma)); - } - ModPath::Nested { names, item, span } => { - let nested = rest.iter() + }, + ModPath::Nested { segments, item, .. } => { + // do any other Nested paths match the current one + let nested = rest + .iter() // filter "self" out .filter(|other_m| other_m != &m) + // filters out Nested we have previously seen + .filter(|other_m| !found_nested.contains(other_m)) // this matches the first path segment and filters non ModPath::Nested items .filter(|other_m| other_m.matches(0, m)) .collect::>(); - println!("{:#?}", nested); - if nested.is_empty() { - path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma)) + path.push_str(&format!("{}::{}{}", segments.join("::").to_string(), item, comma)) + // use mod_a::{mod_b::{one, two}, mod_c::item} } else { - // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2} - let mod_path = if names.len() - 1 > 0 { - ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, } - } else { - ModPath::Item { item: names[0].to_string(), span: *span, } - }; - let names = recursive_path_push(mod_path, comma, &rest, String::new()); - path.push_str(&format!("{}::{{{}}}{}", names, item, comma)) + found_nested.extend(nested.iter()); + found_nested.push(&m); + // we check each segment for matches with other import paths if + // one differs we have to open a new `{}` + for (idx, seg) in segments.iter().enumerate() { + path.push_str(&format!("{}::", seg)); + if nested.iter().all(|other_m| other_m.matches(idx, &m)) { + continue; + } + + path.push_str("{"); + let matched_seg_items = nested + .iter() + .filter(|other_m| !other_m.matches(idx, &m)) + .collect::>(); + for item in matched_seg_items { + if let ModPath::Nested { item, .. } = item { + path.push_str(&format!( + "{}{}", + item, + if nested.len() == idx + 1 { "" } else { ", " } + )); + } + } + path.push_str("}"); + } + path.push_str(&format!("{{{}{}", item, comma)); + for (i, item) in nested.iter().enumerate() { + if let ModPath::Nested { item, segments: matched_seg, .. } = item { + path.push_str(&format!( + "{}{}{}", + if matched_seg > segments { + format!("{}::", matched_seg[segments.len()..].join("::")) + } else { + String::new() + }, + item, + if nested.len() == i + 1 { "" } else { ", " } + )); + } + } + path.push_str("}"); } - } + }, } - count += 1; + count += 1; } if rest_len > 1 { path.push_str("};"); + } else { + path.push_str(";"); } - if let Some(span) = s { + if let Some(span) = attr_span { imports.push((span, path)) + } else { + unreachable!("a span must always be attached to a macro_use attribute") } } - if !self.mac_refs.is_empty() { - // TODO if not empty we found one we could not make a suggestion for - // such as std::prelude::v1 or something else I haven't thought of. - // If we defer the calling of span_lint_and_sugg we can make a decision about its - // applicability? - } else { + // If mac_refs is not empty we have encountered an import we could not handle + // such as `std::prelude::v1::foo` or some other macro that expands to an import. + if self.mac_refs.is_empty() { for (span, import) in imports { let help = format!("use {}", import); span_lint_and_sugg( @@ -237,48 +280,56 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { #[derive(Debug, PartialEq)] enum ModPath { - Item { item: String, span: Span, }, - Nested { names: Vec, item: String, span: Span, }, + Item { + item: String, + span: Span, + }, + Nested { + segments: Vec, + item: String, + span: Span, + }, } impl ModPath { fn span(&self) -> Span { match self { - Self::Item { span, .. } => *span, - Self::Nested { span, .. } => *span, + Self::Item { span, .. } | Self::Nested { span, .. } => *span, } } fn item(&self) -> &str { match self { - Self::Item { item, .. } => item, - Self::Nested { item, .. } => item, + Self::Item { item, .. } | Self::Nested { item, .. } => item, } } fn matches(&self, idx: usize, other: &ModPath) -> bool { match (self, other) { (Self::Item { item, .. }, Self::Item { item: other_item, .. }) => item == other_item, - (Self::Nested { names, .. }, Self::Nested { names: other_names, .. }) => { - match (names.get(idx), other_names.get(idx)) { - (Some(seg), Some(other_seg)) => seg == other_seg, - (_, _) => false, - } - } + ( + Self::Nested { segments, .. }, + Self::Nested { + segments: other_names, .. + }, + ) => match (segments.get(idx), other_names.get(idx)) { + (Some(seg), Some(other_seg)) => seg == other_seg, + (_, _) => false, + }, (_, _) => false, } } } +#[allow(clippy::comparison_chain)] fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap>) { let mut mod_path = import.split("::").collect::>(); if mod_path.len() == 2 { - let item_list = import_map.entry(mod_path[0].to_string()) - .or_insert(vec![]); + let item_list = import_map.entry(mod_path[0].to_string()).or_insert_with(Vec::new); if !item_list.iter().any(|mods| mods.item() == mod_path[1]) { - item_list.push(ModPath::Item{ + item_list.push(ModPath::Item { item: mod_path[1].to_string(), span, }); @@ -288,46 +339,16 @@ fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap String { - match &module { - ModPath::Item { item, .. } => { - path.push_str(&format!("{}{}", item, comma)); - } - ModPath::Nested { names, item, span } => { - let nested = rest.iter() - // filter "self" out - .filter(|other_m| other_m != &&module) - // this matches the first path segment and filters non ModPath::Nested items - .filter(|other_m| other_m.matches(0, &module)) - .collect::>(); - - println!("{:#?}", nested); - - if nested.is_empty() { - path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma)) - } else { - // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2} - let mod_path = if names.len() - 1 > 0 { - ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, } - } else { - ModPath::Item { item: names[0].to_string(), span: *span, } - }; - let names = recursive_path_push(mod_path, comma, rest, path.to_string()); - // path.push_str(&format!("{}{}", item, comma)); - } - } - } - path -} diff --git a/tests/ui/auxiliary/macro_use_helper.rs b/tests/ui/auxiliary/macro_use_helper.rs index 7cc4e1d736a..ecb55d8cb48 100644 --- a/tests/ui/auxiliary/macro_use_helper.rs +++ b/tests/ui/auxiliary/macro_use_helper.rs @@ -13,8 +13,13 @@ pub mod inner { // RE-EXPORT // this will stick in `inner` module + pub use macro_rules::foofoo; pub use macro_rules::try_err; + pub mod nested { + pub use macro_rules::string_add; + } + // ITEM #[macro_export] macro_rules! inner_mod_macro { diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 2d4f71e5d53..52dec0e44b3 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -18,6 +18,8 @@ mod a { use mini_mac; #[macro_use] use mac::inner; + #[macro_use] + use mac::inner::nested; #[derive(ClippyMiniMacroTest)] struct Test; @@ -30,6 +32,8 @@ mod a { let v: ty_macro!() = Vec::default(); inner::try_err!(); + inner::foofoo!(); + nested::string_add!(); } } diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index 6bcacd0be19..00c76c19ea9 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,8 +1,8 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:15:5 + --> $DIR/macro_use_imports.rs:17:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::pub_macro` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use use mini_mac::ClippyMiniMacroTest;` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` @@ -10,37 +10,7 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:15:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner_mod_macro` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro, inner::{foofoo, try_err, nested::string_add}};` -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:15:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::function_macro` - -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:15:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::ty_macro` - -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:15:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::pub_in_private_macro` - -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:17:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest` - -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:19:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::try_err` - -error: aborting due to 7 previous errors +error: aborting due to 2 previous errors From 8c5a5a92ec6f298a067ba052a0d5b6150537c1c9 Mon Sep 17 00:00:00 2001 From: Devin R Date: Tue, 26 May 2020 19:57:36 -0400 Subject: [PATCH 0195/1110] cleaned up import suggestion formatter, look into code reuse with wildcard impotrs --- clippy_lints/src/macro_use.rs | 222 +++++++----------------------- tests/ui/macro_use_imports.stderr | 18 ++- 2 files changed, 63 insertions(+), 177 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 1e1f27e9430..089ae79b02c 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -29,6 +29,12 @@ declare_clippy_lint! { const BRACKETS: &[char] = &['<', '>']; +#[derive(Clone, Debug, PartialEq, Eq)] +struct PathAndSpan { + path: String, + span: Span, +} + /// `MacroRefData` includes the name of the macro /// and the path from `SourceMap::span_to_filename`. #[derive(Debug, Clone)] @@ -110,7 +116,8 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { for kid in cx.tcx.item_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { let span = mac_attr.span; - self.imports.push((cx.tcx.def_path_str(mac_id), span)); + let def_path = cx.tcx.def_path_str(mac_id); + self.imports.push((def_path, span)); } } } else { @@ -147,127 +154,69 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { } #[allow(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { - let mut import_map = FxHashMap::default(); + let mut used = FxHashMap::default(); + let mut check_dup = vec![]; for (import, span) in &self.imports { let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name)); if let Some(idx) = found_idx { let _ = self.mac_refs.remove(idx); - proccess_macro_path(*span, import, &mut import_map); - } - } - // println!("{:#?}", import_map); - let mut imports = vec![]; - for (root, rest) in import_map { - let mut path = format!("use {}::", root); - let mut attr_span = None; - // when a multiple nested paths are found one may be written to the string - // before it is found in this loop so we make note and skip it when this - // loop finds it - let mut found_nested = vec![]; - let mut count = 1; - let rest_len = rest.len(); + let seg = import.split("::").collect::>(); - if rest_len > 1 { - path.push_str("{"); - } - - for m in &rest { - if attr_span.is_none() { - attr_span = Some(m.span()); - } - if found_nested.contains(&m) { - continue; - } - let comma = if rest_len == count { "" } else { ", " }; - match m { - ModPath::Item { item, .. } => { - path.push_str(&format!("{}{}", item, comma)); + match seg.as_slice() { + [] => unreachable!("this should never be empty"), + [_] => unreachable!("path must have two segments ?"), + [root, item] => { + if !check_dup.contains(&item.to_string()) { + used.entry((root.to_string(), span)) + .or_insert(vec![]) + .push(item.to_string()); + check_dup.push(item.to_string()); + } }, - ModPath::Nested { segments, item, .. } => { - // do any other Nested paths match the current one - let nested = rest - .iter() - // filter "self" out - .filter(|other_m| other_m != &m) - // filters out Nested we have previously seen - .filter(|other_m| !found_nested.contains(other_m)) - // this matches the first path segment and filters non ModPath::Nested items - .filter(|other_m| other_m.matches(0, m)) - .collect::>(); - - if nested.is_empty() { - path.push_str(&format!("{}::{}{}", segments.join("::").to_string(), item, comma)) - // use mod_a::{mod_b::{one, two}, mod_c::item} + [root, rest @ ..] => { + if !rest.iter().all(|item| !check_dup.contains(&item.to_string())) { + let mut rest = rest.to_vec(); + rest.sort(); + used.entry((root.to_string(), span)) + .or_insert(vec![]) + .push(rest.join("::")); + check_dup.extend(rest.iter().map(ToString::to_string)); } else { - found_nested.extend(nested.iter()); - found_nested.push(&m); - // we check each segment for matches with other import paths if - // one differs we have to open a new `{}` - for (idx, seg) in segments.iter().enumerate() { - path.push_str(&format!("{}::", seg)); - if nested.iter().all(|other_m| other_m.matches(idx, &m)) { - continue; - } - - path.push_str("{"); - let matched_seg_items = nested - .iter() - .filter(|other_m| !other_m.matches(idx, &m)) - .collect::>(); - for item in matched_seg_items { - if let ModPath::Nested { item, .. } = item { - path.push_str(&format!( - "{}{}", - item, - if nested.len() == idx + 1 { "" } else { ", " } - )); - } - } - path.push_str("}"); - } - path.push_str(&format!("{{{}{}", item, comma)); - for (i, item) in nested.iter().enumerate() { - if let ModPath::Nested { item, segments: matched_seg, .. } = item { - path.push_str(&format!( - "{}{}{}", - if matched_seg > segments { - format!("{}::", matched_seg[segments.len()..].join("::")) - } else { - String::new() - }, - item, - if nested.len() == i + 1 { "" } else { ", " } - )); - } - } - path.push_str("}"); + let mut filtered = rest + .iter() + .filter(|item| !check_dup.contains(&item.to_string())) + .map(ToString::to_string) + .collect::>(); + filtered.sort(); + used.entry((root.to_string(), span)) + .or_insert(vec![]) + .push(filtered.join("::")); + check_dup.extend(filtered); } }, } - count += 1; } - if rest_len > 1 { - path.push_str("};"); + } + + let mut suggestions = vec![]; + for ((root, span), path) in used { + if path.len() == 1 { + suggestions.push((span, format!("{}::{}", root, path[0]))) } else { - path.push_str(";"); - } - if let Some(span) = attr_span { - imports.push((span, path)) - } else { - unreachable!("a span must always be attached to a macro_use attribute") + suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")))) } } // If mac_refs is not empty we have encountered an import we could not handle // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { - for (span, import) in imports { + for (span, import) in suggestions { let help = format!("use {}", import); span_lint_and_sugg( cx, MACRO_USE_IMPORTS, - span, + *span, "`macro_use` attributes are no longer needed in the Rust 2018 edition", "remove the attribute and import the macro directly, try", help, @@ -277,78 +226,3 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { } } } - -#[derive(Debug, PartialEq)] -enum ModPath { - Item { - item: String, - span: Span, - }, - Nested { - segments: Vec, - item: String, - span: Span, - }, -} - -impl ModPath { - fn span(&self) -> Span { - match self { - Self::Item { span, .. } | Self::Nested { span, .. } => *span, - } - } - - fn item(&self) -> &str { - match self { - Self::Item { item, .. } | Self::Nested { item, .. } => item, - } - } - - fn matches(&self, idx: usize, other: &ModPath) -> bool { - match (self, other) { - (Self::Item { item, .. }, Self::Item { item: other_item, .. }) => item == other_item, - ( - Self::Nested { segments, .. }, - Self::Nested { - segments: other_names, .. - }, - ) => match (segments.get(idx), other_names.get(idx)) { - (Some(seg), Some(other_seg)) => seg == other_seg, - (_, _) => false, - }, - (_, _) => false, - } - } -} - -#[allow(clippy::comparison_chain)] -fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap>) { - let mut mod_path = import.split("::").collect::>(); - - if mod_path.len() == 2 { - let item_list = import_map.entry(mod_path[0].to_string()).or_insert_with(Vec::new); - - if !item_list.iter().any(|mods| mods.item() == mod_path[1]) { - item_list.push(ModPath::Item { - item: mod_path[1].to_string(), - span, - }); - } - } else if mod_path.len() > 2 { - let first = mod_path.remove(0); - let name = mod_path.remove(mod_path.len() - 1); - - let nested = ModPath::Nested { - segments: mod_path.into_iter().map(ToString::to_string).collect(), - item: name.to_string(), - span, - }; - // CLIPPY NOTE: this told me to use `or_insert_with(vec![])` - // import_map.entry(first.to_string()).or_insert(vec![]).push(nested); - // which failed as `vec!` is not a closure then told me to add `||` which failed - // with the redundant_closure lint so I finally gave up and used this. - import_map.entry(first.to_string()).or_insert_with(Vec::new).push(nested); - } else { - unreachable!("test to see if code path hit TODO REMOVE") - } -} diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index 00c76c19ea9..83c8ebe6ab9 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -2,15 +2,27 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:17:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use use mini_mac::ClippyMiniMacroTest;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:21:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:19:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{foofoo::inner, inner::try_err}` + error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:15:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro, inner::{foofoo, try_err, nested::string_add}};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro}` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors From 288df59b25c04f05d08d817727a8ac9c8d0d6648 Mon Sep 17 00:00:00 2001 From: Devin R Date: Sun, 7 Jun 2020 16:12:35 -0400 Subject: [PATCH 0196/1110] Fix suggestion output, add run-rustfix to test file, stop sorting import segments duh --- clippy_lints/src/macro_use.rs | 35 +++++++++++++------------- tests/ui/macro_use_imports.fixed | 41 +++++++++++++++++++++++++++++++ tests/ui/macro_use_imports.rs | 1 + tests/ui/macro_use_imports.stderr | 16 ++++++------ 4 files changed, 68 insertions(+), 25 deletions(-) create mode 100644 tests/ui/macro_use_imports.fixed diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 089ae79b02c..7e3ce07254f 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -167,32 +167,33 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { [] => unreachable!("this should never be empty"), [_] => unreachable!("path must have two segments ?"), [root, item] => { - if !check_dup.contains(&item.to_string()) { + if !check_dup.contains(&(*item).to_string()) { used.entry((root.to_string(), span)) - .or_insert(vec![]) + .or_insert_with(|| vec![]) .push(item.to_string()); check_dup.push(item.to_string()); } }, [root, rest @ ..] => { - if !rest.iter().all(|item| !check_dup.contains(&item.to_string())) { - let mut rest = rest.to_vec(); - rest.sort(); - used.entry((root.to_string(), span)) - .or_insert(vec![]) - .push(rest.join("::")); - check_dup.extend(rest.iter().map(ToString::to_string)); - } else { - let mut filtered = rest + if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) { + let filtered = rest .iter() - .filter(|item| !check_dup.contains(&item.to_string())) - .map(ToString::to_string) + .filter_map(|item| if check_dup.contains(&(*item).to_string()) { + None + } else { + Some(item.to_string()) + }) .collect::>(); - filtered.sort(); - used.entry((root.to_string(), span)) - .or_insert(vec![]) + used.entry(((*root).to_string(), span)) + .or_insert_with(|| vec![]) .push(filtered.join("::")); check_dup.extend(filtered); + } else { + let rest = rest.to_vec(); + used.entry((root.to_string(), span)) + .or_insert_with(|| vec![]) + .push(rest.join("::")); + check_dup.extend(rest.iter().map(ToString::to_string)); } }, } @@ -212,7 +213,7 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { for (span, import) in suggestions { - let help = format!("use {}", import); + let help = format!("use {};", import); span_lint_and_sugg( cx, MACRO_USE_IMPORTS, diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed new file mode 100644 index 00000000000..8034c56b59a --- /dev/null +++ b/tests/ui/macro_use_imports.fixed @@ -0,0 +1,41 @@ +// compile-flags: --edition 2018 +// aux-build:macro_rules.rs +// aux-build:macro_use_helper.rs +// run-rustfix + +#![allow(clippy::single_component_path_imports)] +#![warn(clippy::macro_use_imports)] + +#[macro_use] +extern crate macro_use_helper as mac; + +#[macro_use] +extern crate clippy_mini_macro_test as mini_mac; + +mod a { + use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro}; + use mac; + use mini_mac::ClippyMiniMacroTest; + use mini_mac; + use mac::{inner::foofoo, inner::try_err}; + use mac::inner; + use mac::inner::nested::string_add; + use mac::inner::nested; + + #[derive(ClippyMiniMacroTest)] + struct Test; + + fn test() { + pub_macro!(); + inner_mod_macro!(); + pub_in_private_macro!(_var); + function_macro!(); + let v: ty_macro!() = Vec::default(); + + inner::try_err!(); + inner::foofoo!(); + nested::string_add!(); + } +} + +fn main() {} diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 52dec0e44b3..7d415222d64 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -1,6 +1,7 @@ // compile-flags: --edition 2018 // aux-build:macro_rules.rs // aux-build:macro_use_helper.rs +// run-rustfix #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index 83c8ebe6ab9..6feda8a5222 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,28 +1,28 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:17:5 + --> $DIR/macro_use_imports.rs:18:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:21:5 + --> $DIR/macro_use_imports.rs:20:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:19:5 + --> $DIR/macro_use_imports.rs:16:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{foofoo::inner, inner::try_err}` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:15:5 + --> $DIR/macro_use_imports.rs:22:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro}` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` error: aborting due to 4 previous errors From e521a4ed3818c02a1831db570e69d056c351ee05 Mon Sep 17 00:00:00 2001 From: Devin R Date: Sun, 7 Jun 2020 16:25:21 -0400 Subject: [PATCH 0197/1110] Add enough attrs to the test file so the fix compiles with no errors, fmt/`clippy` --- clippy_lints/src/macro_use.rs | 29 ++++++++++++++++------------- tests/ui/macro_use_imports.fixed | 2 ++ tests/ui/macro_use_imports.rs | 2 ++ tests/ui/macro_use_imports.stderr | 16 ++++++++-------- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 7e3ce07254f..b845b20d2c0 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -164,34 +164,37 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { let seg = import.split("::").collect::>(); match seg.as_slice() { - [] => unreachable!("this should never be empty"), - [_] => unreachable!("path must have two segments ?"), + // an empty path is impossible + // a path should always consist of 2 or more segments + [] | [_] => return, [root, item] => { if !check_dup.contains(&(*item).to_string()) { - used.entry((root.to_string(), span)) - .or_insert_with(|| vec![]) - .push(item.to_string()); - check_dup.push(item.to_string()); + used.entry(((*root).to_string(), span)) + .or_insert_with(Vec::new) + .push((*item).to_string()); + check_dup.push((*item).to_string()); } }, [root, rest @ ..] => { if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) { let filtered = rest .iter() - .filter_map(|item| if check_dup.contains(&(*item).to_string()) { - None - } else { - Some(item.to_string()) + .filter_map(|item| { + if check_dup.contains(&(*item).to_string()) { + None + } else { + Some((*item).to_string()) + } }) .collect::>(); used.entry(((*root).to_string(), span)) - .or_insert_with(|| vec![]) + .or_insert_with(Vec::new) .push(filtered.join("::")); check_dup.extend(filtered); } else { let rest = rest.to_vec(); - used.entry((root.to_string(), span)) - .or_insert_with(|| vec![]) + used.entry(((*root).to_string(), span)) + .or_insert_with(Vec::new) .push(rest.join("::")); check_dup.extend(rest.iter().map(ToString::to_string)); } diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 8034c56b59a..91e34c62160 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -2,7 +2,9 @@ // aux-build:macro_rules.rs // aux-build:macro_use_helper.rs // run-rustfix +// ignore-32bit +#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 7d415222d64..9c3c50c5d49 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -2,7 +2,9 @@ // aux-build:macro_rules.rs // aux-build:macro_use_helper.rs // run-rustfix +// ignore-32bit +#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index 6feda8a5222..f8c86c8d917 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -2,7 +2,7 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:18:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` @@ -10,17 +10,17 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:20:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` - -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:16:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:22:5 | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:24:5 + | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` From a083b84b783a2c9e622c9d618e751da22adaff37 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 9 Jun 2020 23:49:21 +0200 Subject: [PATCH 0198/1110] if_same_then_else: don't assume multiplication is always commutative --- clippy_lints/src/utils/hir_utils.rs | 13 +++++-------- tests/ui/if_same_then_else.rs | 12 ++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index f8d197c15e8..6846658b6e2 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -309,18 +309,15 @@ fn swap_binop<'a>( rhs: &'a Expr<'a>, ) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> { match binop { - BinOpKind::Add - | BinOpKind::Mul - | BinOpKind::Eq - | BinOpKind::Ne - | BinOpKind::BitAnd - | BinOpKind::BitXor - | BinOpKind::BitOr => Some((binop, rhs, lhs)), + BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => { + Some((binop, rhs, lhs)) + }, BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)), BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)), BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)), BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)), - BinOpKind::Shl + BinOpKind::Mul + | BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Rem | BinOpKind::Sub diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 6bbf79edfcf..9c5fe02f751 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -142,4 +142,16 @@ fn func() { fn f(val: &[u8]) {} +mod issue_5698 { + fn mul_not_always_commutative(x: i32, y: i32) -> i32 { + if x == 42 { + x * y + } else if x == 21 { + y * x + } else { + 0 + } + } +} + fn main() {} From 2f74283fce768a262387fe7f51e1e4ebb9b0e300 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 10 Jun 2020 00:14:02 +0200 Subject: [PATCH 0199/1110] Add a comment linking to the issue --- clippy_lints/src/utils/hir_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 6846658b6e2..9c2c96203c0 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -316,7 +316,7 @@ fn swap_binop<'a>( BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)), BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)), BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)), - BinOpKind::Mul + BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698 | BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Rem From 56f25e3e62dbdd8f84a9152bbfb73a35055363dd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 10 Jun 2020 19:29:11 -0700 Subject: [PATCH 0200/1110] Downgrade unnested_or_patterns to pedantic --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/unnested_or_patterns.rs | 2 +- src/lintlist/mod.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 021fbe932d8..19e1b00050a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1187,6 +1187,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::OPTION_OPTION), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unused_self::UNUSED_SELF), LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), @@ -1440,7 +1441,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1624,7 +1624,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 8c281126c32..4d3682263f1 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// ``` pub UNNESTED_OR_PATTERNS, - complexity, + pedantic, "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index cac3cc6bdb3..edceb755180 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2329,7 +2329,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "unnested_or_patterns", - group: "complexity", + group: "pedantic", desc: "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`", deprecation: None, module: "unnested_or_patterns", From 840786a93976d5885bfe6c7878cecc99e4a56432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 1 Jun 2020 00:22:29 +0200 Subject: [PATCH 0201/1110] clippy-driver: pass all args after "--rustc" to rustc. --- src/driver.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 4453ae5ce44..1956effa827 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -297,12 +297,6 @@ pub fn main() { exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); - if orig_args.iter().any(|a| a == "--version" || a == "-V") { - let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); - exit(0); - } - // Get the sysroot, looking from most specific to this invocation to the least: // - command line // - runtime environment @@ -348,6 +342,29 @@ pub fn main() { .map(|pb| pb.to_string_lossy().to_string()) .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); + // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" + // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver + // uses + if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") { + orig_args.remove(pos); + orig_args[0] = "rustc".to_string(); + + // if we call "rustc", we need to pass --sysroot here as well + let mut args: Vec = orig_args.clone(); + if !have_sys_root_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + + println!("args: {:?}", args); + return rustc_driver::run_compiler(&args, &mut DefaultCallbacks, None, None); + } + + if orig_args.iter().any(|a| a == "--version" || a == "-V") { + let version_info = rustc_tools_util::get_version_info!(); + println!("{}", version_info); + exit(0); + } + // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. // We're invoking the compiler programmatically, so we ignore this/ let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); From 88ab10400b81338782c729e4193e47cd3ef1cab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 2 Jun 2020 14:50:44 +0200 Subject: [PATCH 0202/1110] add test and remove debug print --- .github/driver.sh | 5 +++++ src/driver.rs | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/driver.sh b/.github/driver.sh index a2e87f5eb37..c797bdb14db 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -26,4 +26,9 @@ unset CARGO_MANIFEST_DIR sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr diff normalized.stderr tests/ui/cstring.stderr + +# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same +SYSROOT=`rustc --print sysroot` +diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) + # TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR diff --git a/src/driver.rs b/src/driver.rs index 1956effa827..5ef8d3cf809 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -355,7 +355,6 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; - println!("args: {:?}", args); return rustc_driver::run_compiler(&args, &mut DefaultCallbacks, None, None); } From f1d5cd5d13bbd59c21e70e2e728c3db9829cf816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 7 Jun 2020 16:27:41 +0200 Subject: [PATCH 0203/1110] add test for compiler output when compiling with rustc/clippy-driver --- .github/driver.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/driver.sh b/.github/driver.sh index c797bdb14db..f8139f36369 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -31,4 +31,9 @@ diff normalized.stderr tests/ui/cstring.stderr SYSROOT=`rustc --print sysroot` diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) +# we can't run 2 rustcs on the same file at the same time +CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver tests/driver/main.rs` +RUSTC=`rustc tests/driver/main.rs` +diff <($CLIPPY) <($RUSTC) + # TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR From 7a62380fc8f8ca39bc49b8f67a4d4929911cb036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 9 Jun 2020 13:18:00 +0200 Subject: [PATCH 0204/1110] clippy-driver: fix test and add --rustc to --help output --- .github/driver.sh | 6 ++++-- src/driver.rs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/driver.sh b/.github/driver.sh index f8139f36369..2c17c4203ae 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -31,9 +31,11 @@ diff normalized.stderr tests/ui/cstring.stderr SYSROOT=`rustc --print sysroot` diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) + +echo "fn main() {}" > target/driver_test.rs # we can't run 2 rustcs on the same file at the same time -CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver tests/driver/main.rs` -RUSTC=`rustc tests/driver/main.rs` +CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc` +RUSTC=`rustc ./target/driver_test.rs` diff <($CLIPPY) <($RUSTC) # TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR diff --git a/src/driver.rs b/src/driver.rs index 5ef8d3cf809..6faa5e9fe66 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -207,6 +207,7 @@ Usage: Common options: -h, --help Print this message + --rustc Pass all args to rustc -V, --version Print version info and exit Other options are the same as `cargo check`. From b21ef2b365ef4f5c973327f3f6064b8be42d1dae Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 13 Jun 2020 00:52:32 +0200 Subject: [PATCH 0205/1110] Fix ICE in consts::binop --- clippy_lints/src/consts.rs | 2 +- tests/ui/crashes/ice-5389.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-5389.rs diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 81ddc8c0067..e3c6908b76b 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -396,7 +396,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match self.tables.expr_ty(left).kind { + (Constant::Int(l), Some(Constant::Int(r))) => match self.tables.expr_ty_opt(left)?.kind { ty::Int(ity) => { let l = sext(self.lcx.tcx, l, ity); let r = sext(self.lcx.tcx, r, ity); diff --git a/tests/ui/crashes/ice-5389.rs b/tests/ui/crashes/ice-5389.rs new file mode 100644 index 00000000000..de262199004 --- /dev/null +++ b/tests/ui/crashes/ice-5389.rs @@ -0,0 +1,13 @@ +#![allow(clippy::explicit_counter_loop)] + +fn main() { + let v = vec![1, 2, 3]; + let mut i = 0; + let max_storage_size = [0; 128 * 1024]; + for item in &v { + bar(i, *item); + i += 1; + } +} + +fn bar(_: usize, _: u32) {} From dee794f4503dda6a10891b49a7cb2f8bb92e001b Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sat, 13 Jun 2020 12:42:58 +0200 Subject: [PATCH 0206/1110] typo --- clippy_lints/src/new_without_default.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index dd236535c18..42200385932 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// To fix the lint, and a `Default` implementation that delegates to `new`: + /// To fix the lint, add a `Default` implementation that delegates to `new`: /// /// ```ignore /// struct Foo(Bar); From f663a21c8f51db58ff73e2e0d9852d03e8916a5e Mon Sep 17 00:00:00 2001 From: Iain Brandram-Adams Date: Sun, 14 Jun 2020 01:24:36 +1200 Subject: [PATCH 0207/1110] Remove `bar` from blacklisted names --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/blacklisted_name.rs | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9e8e0ff30ec..418c4ded544 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -107,7 +107,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about - (blacklisted_names, "blacklisted_names": Vec, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()), + (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index ca9d8d16b78..3d87eb06181 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -12,29 +12,32 @@ fn test(foo: ()) {} fn main() { let foo = 42; - let bar = 42; let baz = 42; + let quux = 42; + // Unlike these others, `bar` is considered an acceptable name to use. + // See https://github.com/rust-lang/rust-clippy/issues/5225. - let barb = 42; - let barbaric = 42; + let food = 42; + let foodstuffs = 42; + let bazaar = 42; match (42, Some(1337), Some(0)) { - (foo, Some(bar), baz @ Some(_)) => (), + (foo, Some(baz), quux @ Some(_)) => (), _ => (), } } fn issue_1647(mut foo: u8) { - let mut bar = 0; - if let Some(mut baz) = Some(42) {} + let mut baz = 0; + if let Some(mut quux) = Some(42) {} } fn issue_1647_ref() { - let ref bar = 0; - if let Some(ref baz) = Some(42) {} + let ref baz = 0; + if let Some(ref quux) = Some(42) {} } fn issue_1647_ref_mut() { - let ref mut bar = 0; - if let Some(ref mut baz) = Some(42) {} + let ref mut baz = 0; + if let Some(ref mut quux) = Some(42) {} } From 40ee620e51c86c72e3c2b65df71f5f0a4a79797f Mon Sep 17 00:00:00 2001 From: Teddy_Wang Date: Mon, 8 Jun 2020 00:35:10 -0400 Subject: [PATCH 0208/1110] Added a lint for .map(|x| x) --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/map_identity.rs | 126 +++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/map_flatten.stderr | 4 +- tests/ui/map_identity.fixed | 23 ++++++ tests/ui/map_identity.rs | 25 ++++++ tests/ui/map_identity.stderr | 37 +++++++++ 10 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/map_identity.rs create mode 100644 tests/ui/map_identity.fixed create mode 100644 tests/ui/map_identity.rs create mode 100644 tests/ui/map_identity.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index adc945a6944..d6186c319d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1508,6 +1508,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 021fbe932d8..8b5e0b84eb4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -252,6 +252,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod map_clone; +mod map_identity; mod map_unit_fn; mod match_on_vec_items; mod matches; @@ -631,6 +632,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, + &map_identity::MAP_IDENTITY, &map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN, &match_on_vec_items::MATCH_ON_VEC_ITEMS, @@ -1080,6 +1082,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_late_pass(|| box macro_use::MacroUseImports::default()); + store.register_late_pass(|| box map_identity::MapIdentity); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1295,6 +1298,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), @@ -1573,6 +1577,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), + LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&matches::MATCH_AS_REF), diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs new file mode 100644 index 00000000000..9bc8f1bec1b --- /dev/null +++ b/clippy_lints/src/map_identity.rs @@ -0,0 +1,126 @@ +use crate::utils::{ + is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks, + span_lint_and_sugg, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function. + /// + /// **Why is this bad?** It can be written more concisely without the call to `map`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x = [1, 2, 3]; + /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect(); + /// ``` + /// Use instead: + /// ```rust + /// let x = [1, 2, 3]; + /// let y: Vec<_> = x.iter().map(|x| 2*x).collect(); + /// ``` + pub MAP_IDENTITY, + complexity, + "using iterator.map(|x| x)" +} + +declare_lint_pass!(MapIdentity => [MAP_IDENTITY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapIdentity { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let Some([caller, func]) = get_map_argument(cx, expr); + if is_expr_identity_function(cx, func); + then { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + expr.span.trim_start(caller.span).unwrap(), + "unnecessary map of the identity function", + "remove the call to `map`", + String::new(), + Applicability::MachineApplicable + ) + } + } + } +} + +/// Returns the arguments passed into map() if the expression is a method call to +/// map(). Otherwise, returns None. +fn get_map_argument<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> { + if_chain! { + if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind; + if args.len() == 2 && method.ident.as_str() == "map"; + let caller_ty = cx.tables.expr_ty(&args[0]); + if match_trait_method(cx, expr, &paths::ITERATOR) + || is_type_diagnostic_item(cx, caller_ty, sym!(result_type)) + || is_type_diagnostic_item(cx, caller_ty, sym!(option_type)); + then { + Some(args) + } else { + None + } + } +} + +/// Checks if an expression represents the identity function +/// Only examines closures and `std::convert::identity` +fn is_expr_identity_function(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), + ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), + _ => false, + } +} + +/// Checks if a function's body represents the identity function +/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| { +/// return x; }` +fn is_body_identity_function(cx: &LateContext<'_, '_>, func: &Body<'_>) -> bool { + let params = func.params; + let body = remove_blocks(&func.value); + + // if there's less/more than one parameter, then it is not the identity function + if params.len() != 1 { + return false; + } + + match body.kind { + ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat), + ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat), + ExprKind::Block(ref block, _) => { + if_chain! { + if block.stmts.len() == 1; + if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind; + if let ExprKind::Ret(Some(ref ret_val)) = expr.kind; + then { + match_expr_param(cx, ret_val, params[0].pat) + } else { + false + } + } + }, + _ => false, + } +} + +/// Returns true iff an expression returns the same thing as a parameter's pattern +fn match_expr_param(cx: &LateContext<'_, '_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool { + if let PatKind::Binding(_, _, ident, _) = pat.kind { + match_var(expr, ident.name) && !(cx.tables.hir_owner == Some(expr.hir_id.owner) && is_adjusted(cx, expr)) + } else { + false + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index cac3cc6bdb3..a9cd5469048 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1144,6 +1144,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "map_identity", + group: "complexity", + desc: "using iterator.map(|x| x)", + deprecation: None, + module: "map_identity", + }, Lint { name: "map_unwrap_or", group: "pedantic", diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 7ac368878ab..4171d80f48a 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -2,6 +2,7 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::map_identity)] fn main() { let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index a608601039c..16a0fd090ad 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -2,6 +2,7 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::map_identity)] fn main() { let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 3cf2abd5b6d..00bc41c15e9 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` - --> $DIR/map_flatten.rs:7:21 + --> $DIR/map_flatten.rs:8:21 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` @@ -7,7 +7,7 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().colle = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:8:24 + --> $DIR/map_flatten.rs:9:24 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed new file mode 100644 index 00000000000..4a1452b25f3 --- /dev/null +++ b/tests/ui/map_identity.fixed @@ -0,0 +1,23 @@ +// run-rustfix +#![warn(clippy::map_identity)] +#![allow(clippy::needless_return)] + +fn main() { + let x: [u16; 3] = [1, 2, 3]; + // should lint + let _: Vec<_> = x.iter().map(not_identity).collect(); + let _: Vec<_> = x.iter().collect(); + let _: Option = Some(3); + let _: Result = Ok(-3); + // should not lint + let _: Vec<_> = x.iter().map(|x| 2 * x).collect(); + let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect(); + let _: Option = None.map(|x: u8| x - 1); + let _: Result = Err(2.3).map(|x: i8| { + return x + 3; + }); +} + +fn not_identity(x: &u16) -> u16 { + *x +} diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs new file mode 100644 index 00000000000..65c7e6e1ea5 --- /dev/null +++ b/tests/ui/map_identity.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::map_identity)] +#![allow(clippy::needless_return)] + +fn main() { + let x: [u16; 3] = [1, 2, 3]; + // should lint + let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); + let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); + let _: Option = Some(3).map(|x| x); + let _: Result = Ok(-3).map(|x| { + return x; + }); + // should not lint + let _: Vec<_> = x.iter().map(|x| 2 * x).collect(); + let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect(); + let _: Option = None.map(|x: u8| x - 1); + let _: Result = Err(2.3).map(|x: i8| { + return x + 3; + }); +} + +fn not_identity(x: &u16) -> u16 { + *x +} diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr new file mode 100644 index 00000000000..e4a0320cbda --- /dev/null +++ b/tests/ui/map_identity.stderr @@ -0,0 +1,37 @@ +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:8:47 + | +LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); + | ^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + | + = note: `-D clippy::map-identity` implied by `-D warnings` + +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:9:57 + | +LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:9:29 + | +LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:10:32 + | +LL | let _: Option = Some(3).map(|x| x); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:11:36 + | +LL | let _: Result = Ok(-3).map(|x| { + | ____________________________________^ +LL | | return x; +LL | | }); + | |______^ help: remove the call to `map` + +error: aborting due to 5 previous errors + From c020e45cd3c1f10d1271de8b47cea9e00b73f500 Mon Sep 17 00:00:00 2001 From: Iain Brandram-Adams Date: Sun, 14 Jun 2020 12:40:36 +1200 Subject: [PATCH 0209/1110] Update stderr to match, and reinforce comments --- tests/ui/blacklisted_name.rs | 4 +- tests/ui/blacklisted_name.stderr | 86 ++++++++++++++++---------------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index 3d87eb06181..cb15bdd2f1b 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -14,8 +14,10 @@ fn main() { let foo = 42; let baz = 42; let quux = 42; - // Unlike these others, `bar` is considered an acceptable name to use. + // Unlike these others, `bar` is actually considered an acceptable name. + // Among many other legitimate uses, bar commonly refers to a period of time in music. // See https://github.com/rust-lang/rust-clippy/issues/5225. + let bar = 42; let food = 42; let foodstuffs = 42; diff --git a/tests/ui/blacklisted_name.stderr b/tests/ui/blacklisted_name.stderr index 44123829fb0..70dbdaece8b 100644 --- a/tests/ui/blacklisted_name.stderr +++ b/tests/ui/blacklisted_name.stderr @@ -12,77 +12,77 @@ error: use of a blacklisted/placeholder name `foo` LL | let foo = 42; | ^^^ -error: use of a blacklisted/placeholder name `bar` - --> $DIR/blacklisted_name.rs:15:9 - | -LL | let bar = 42; - | ^^^ - error: use of a blacklisted/placeholder name `baz` - --> $DIR/blacklisted_name.rs:16:9 + --> $DIR/blacklisted_name.rs:15:9 | LL | let baz = 42; | ^^^ -error: use of a blacklisted/placeholder name `foo` - --> $DIR/blacklisted_name.rs:22:10 +error: use of a blacklisted/placeholder name `quux` + --> $DIR/blacklisted_name.rs:16:9 | -LL | (foo, Some(bar), baz @ Some(_)) => (), +LL | let quux = 42; + | ^^^^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:27:10 + | +LL | (foo, Some(baz), quux @ Some(_)) => (), | ^^^ -error: use of a blacklisted/placeholder name `bar` - --> $DIR/blacklisted_name.rs:22:20 +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:27:20 | -LL | (foo, Some(bar), baz @ Some(_)) => (), +LL | (foo, Some(baz), quux @ Some(_)) => (), | ^^^ -error: use of a blacklisted/placeholder name `baz` - --> $DIR/blacklisted_name.rs:22:26 +error: use of a blacklisted/placeholder name `quux` + --> $DIR/blacklisted_name.rs:27:26 | -LL | (foo, Some(bar), baz @ Some(_)) => (), - | ^^^ +LL | (foo, Some(baz), quux @ Some(_)) => (), + | ^^^^ error: use of a blacklisted/placeholder name `foo` - --> $DIR/blacklisted_name.rs:27:19 + --> $DIR/blacklisted_name.rs:32:19 | LL | fn issue_1647(mut foo: u8) { | ^^^ -error: use of a blacklisted/placeholder name `bar` - --> $DIR/blacklisted_name.rs:28:13 - | -LL | let mut bar = 0; - | ^^^ - error: use of a blacklisted/placeholder name `baz` - --> $DIR/blacklisted_name.rs:29:21 - | -LL | if let Some(mut baz) = Some(42) {} - | ^^^ - -error: use of a blacklisted/placeholder name `bar` --> $DIR/blacklisted_name.rs:33:13 | -LL | let ref bar = 0; +LL | let mut baz = 0; | ^^^ -error: use of a blacklisted/placeholder name `baz` +error: use of a blacklisted/placeholder name `quux` --> $DIR/blacklisted_name.rs:34:21 | -LL | if let Some(ref baz) = Some(42) {} - | ^^^ - -error: use of a blacklisted/placeholder name `bar` - --> $DIR/blacklisted_name.rs:38:17 - | -LL | let ref mut bar = 0; - | ^^^ +LL | if let Some(mut quux) = Some(42) {} + | ^^^^ error: use of a blacklisted/placeholder name `baz` - --> $DIR/blacklisted_name.rs:39:25 + --> $DIR/blacklisted_name.rs:38:13 | -LL | if let Some(ref mut baz) = Some(42) {} - | ^^^ +LL | let ref baz = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `quux` + --> $DIR/blacklisted_name.rs:39:21 + | +LL | if let Some(ref quux) = Some(42) {} + | ^^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:43:17 + | +LL | let ref mut baz = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `quux` + --> $DIR/blacklisted_name.rs:44:25 + | +LL | if let Some(ref mut quux) = Some(42) {} + | ^^^^ error: aborting due to 14 previous errors From 454ed47acf462c847956464ac3811546b733bd5b Mon Sep 17 00:00:00 2001 From: Iain Brandram-Adams Date: Sun, 14 Jun 2020 12:46:56 +1200 Subject: [PATCH 0210/1110] Update comment in conf.rs --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 418c4ded544..c41befbf147 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about + /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25), From 8a6f42a9707bbad1dad3f1511f793cd07c723bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 14 Jun 2020 11:07:44 +0200 Subject: [PATCH 0211/1110] Fix typo in wildcard_imports --- clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index b637253bd02..79f7705e281 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for wildcard imports `use _::*`. /// - /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if + /// **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if /// you try to import something through a wildcard, that already has been imported by name from /// a different source: /// From 8c1ee063bb67b20ac17603d0f0025b48b958cc08 Mon Sep 17 00:00:00 2001 From: Ericko Samudera Date: Mon, 8 Jun 2020 00:44:14 +0700 Subject: [PATCH 0212/1110] mem_replace_with_uninit: suggest std::ptr::read --- clippy_lints/src/mem_replace.rs | 82 ++++++++++++++++++++++----------- tests/ui/repl_uninit.rs | 6 +++ tests/ui/repl_uninit.stderr | 19 ++++---- 3 files changed, 71 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index ab6865bf0f3..e2672e02b36 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -135,33 +135,59 @@ fn check_replace_option_with_none(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest } } -fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, expr_span: Span) { - if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind { - if_chain! { - if repl_args.is_empty(); - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - then { - if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { - span_lint_and_help( - cx, - MEM_REPLACE_WITH_UNINIT, - expr_span, - "replacing with `mem::uninitialized()`", - None, - "consider using the `take_mut` crate instead", - ); - } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) && - !cx.tables.expr_ty(src).is_primitive() { - span_lint_and_help( - cx, - MEM_REPLACE_WITH_UNINIT, - expr_span, - "replacing with `mem::zeroed()`", - None, - "consider using a default value or the `take_mut` crate instead", - ); - } +fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { + if_chain! { + // check if replacement is mem::MaybeUninit::uninit().assume_init() + if let Some(method_def_id) = cx.tables.type_dependent_def_id(src.hir_id); + if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::MaybeUninit::uninit().assume_init()`", + "consider using", + format!( + "std::ptr::read({})", + snippet_with_applicability(cx, dest.span, "", &mut applicability) + ), + applicability, + ); + return; + } + } + + if_chain! { + if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind; + if repl_args.is_empty(); + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + then { + if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::uninitialized()`", + "consider using", + format!( + "std::ptr::read({})", + snippet_with_applicability(cx, dest.span, "", &mut applicability) + ), + applicability, + ); + } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) && + !cx.tables.expr_ty(src).is_primitive() { + span_lint_and_help( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::zeroed()`", + None, + "consider using a default value or the `take_mut` crate instead", + ); } } } @@ -209,7 +235,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace { if let [dest, src] = &**func_args; then { check_replace_option_with_none(cx, src, dest, expr.span); - check_replace_with_uninit(cx, src, expr.span); + check_replace_with_uninit(cx, src, dest, expr.span); check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/tests/ui/repl_uninit.rs b/tests/ui/repl_uninit.rs index 346972b7bb4..ad5b8e4857d 100644 --- a/tests/ui/repl_uninit.rs +++ b/tests/ui/repl_uninit.rs @@ -17,6 +17,12 @@ fn main() { std::mem::forget(mem::replace(&mut v, new_v)); } + unsafe { + let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init()); + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + unsafe { let taken_v = mem::replace(&mut v, mem::zeroed()); let new_v = might_panic(taken_v); diff --git a/tests/ui/repl_uninit.stderr b/tests/ui/repl_uninit.stderr index c1f55d7601e..09468eeaea4 100644 --- a/tests/ui/repl_uninit.stderr +++ b/tests/ui/repl_uninit.stderr @@ -2,13 +2,18 @@ error: replacing with `mem::uninitialized()` --> $DIR/repl_uninit.rs:15:23 | LL | let taken_v = mem::replace(&mut v, mem::uninitialized()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)` | = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings` - = help: consider using the `take_mut` crate instead + +error: replacing with `mem::MaybeUninit::uninit().assume_init()` + --> $DIR/repl_uninit.rs:21:23 + | +LL | let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)` error: replacing with `mem::zeroed()` - --> $DIR/repl_uninit.rs:21:23 + --> $DIR/repl_uninit.rs:27:23 | LL | let taken_v = mem::replace(&mut v, mem::zeroed()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,12 +21,10 @@ LL | let taken_v = mem::replace(&mut v, mem::zeroed()); = help: consider using a default value or the `take_mut` crate instead error: replacing with `mem::uninitialized()` - --> $DIR/repl_uninit.rs:33:28 + --> $DIR/repl_uninit.rs:39:28 | LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using the `take_mut` crate instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(uref)` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From f7acea2683c6124854bfe20e7127e4dfba344d3e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 24 Apr 2020 00:14:03 +0200 Subject: [PATCH 0213/1110] Register redundant_field_names and non_expressive_names as early passes --- clippy_lints/src/lib.rs | 12 ++++++------ src/driver.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b8415fa3af1..9057de99029 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -341,13 +341,8 @@ mod reexport { /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. /// /// Used in `./src/driver.rs`. -pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Conf) { +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); - store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; - store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames { - single_char_binding_names_threshold, - }); store.register_pre_expansion_pass(|| box attrs::DeprecatedCfgAttribute); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); } @@ -1051,6 +1046,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unnamed_address::UnnamedAddress); store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box future_not_send::FutureNotSend); + store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; + store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { + single_char_binding_names_threshold, + }); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/src/driver.rs b/src/driver.rs index 2c699998ea9..928497ba5e4 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -79,7 +79,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_lints::read_conf(&[], &sess); clippy_lints::register_plugins(&mut lint_store, &sess, &conf); - clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf); + clippy_lints::register_pre_expansion_lints(&mut lint_store); clippy_lints::register_renamed(&mut lint_store); })); From 485229c4a3d6a2fbe40f5a6976a33144a27497c6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 May 2020 16:26:55 +0200 Subject: [PATCH 0214/1110] Fix fallout in redundant_field_names --- clippy_lints/src/redundant_field_names.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index b12c3c344ef..2a81170e49e 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -2,6 +2,7 @@ use crate::utils::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -36,6 +37,9 @@ declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess, expr.span) { + return; + } if let ExprKind::Struct(_, ref fields, _) = expr.kind { for field in fields { if field.is_shorthand { From efd3dcff97f67f376e354c047133ce9044c52991 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 May 2020 16:50:00 +0200 Subject: [PATCH 0215/1110] Fix fallout in similar_names --- clippy_lints/src/non_expressive_names.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 45809b35986..ef3b1da1b0b 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -5,6 +5,7 @@ use rustc_ast::ast::{ use rustc_ast::attr; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::SymbolStr; @@ -354,12 +355,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { impl EarlyLintPass for NonExpressiveNames { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if in_external_macro(cx.sess, item.span) { + return; + } + if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } } fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) { + if in_external_macro(cx.sess, item.span) { + return; + } + if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } From bb37a0f948b02e6434dbe3ea615960052d37f784 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 27 May 2020 00:06:50 +0200 Subject: [PATCH 0216/1110] Avoid triggering similar names on code from expansion --- clippy_lints/src/new_without_default.rs | 10 +++++----- clippy_lints/src/non_expressive_names.rs | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 19e06ab66c4..1ad631abe91 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -126,8 +126,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { return; } if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) { - let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); - let self_ty = cx.tcx.type_of(self_did); + let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); + let self_ty = cx.tcx.type_of(self_def_id); if_chain! { if same_tys(cx, self_ty, return_ty(cx, id)); if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); @@ -148,10 +148,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { // generics if_chain! { if let Some(ref impling_types) = self.impling_types; - if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def(); - if let Some(self_def_id) = self_def.did.as_local(); + if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def(); + if let Some(self_local_did) = self_def.did.as_local(); then { - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id); + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); if impling_types.contains(&self_id) { return; } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index ef3b1da1b0b..5331bf26e05 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -132,7 +132,11 @@ struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { fn visit_pat(&mut self, pat: &'tcx Pat) { match pat.kind { - PatKind::Ident(_, ident, _) => self.check_ident(ident), + PatKind::Ident(_, ident, _) => { + if !pat.span.from_expansion() { + self.check_ident(ident); + } + }, PatKind::Struct(_, ref fields, _) => { for field in fields { if !field.is_shorthand { From a7743e9084f9ccd7c966f98a14fa667c694d66ab Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 17 Jun 2020 00:32:47 +0200 Subject: [PATCH 0217/1110] redundant_pattern_matching: avoid non-const fn in const context --- .../src/redundant_pattern_matching.rs | 80 ++++++++++++++----- tests/ui/redundant_pattern_matching.fixed | 42 ++++++++++ tests/ui/redundant_pattern_matching.rs | 42 ++++++++++ tests/ui/redundant_pattern_matching.stderr | 56 ++++++------- ...undant_pattern_matching_const_result.fixed | 46 +++++++++++ ...redundant_pattern_matching_const_result.rs | 52 ++++++++++++ ...ndant_pattern_matching_const_result.stderr | 46 +++++++++++ 7 files changed, 318 insertions(+), 46 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_const_result.fixed create mode 100644 tests/ui/redundant_pattern_matching_const_result.rs create mode 100644 tests/ui/redundant_pattern_matching_const_result.stderr diff --git a/clippy_lints/src/redundant_pattern_matching.rs b/clippy_lints/src/redundant_pattern_matching.rs index 7ee298e9833..b95b12c4eb5 100644 --- a/clippy_lints/src/redundant_pattern_matching.rs +++ b/clippy_lints/src/redundant_pattern_matching.rs @@ -1,10 +1,13 @@ -use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; +use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_mir::const_eval::is_const_fn; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Symbol; declare_clippy_lint! { /// **What it does:** Lint for redundant pattern matching over `Result` or @@ -64,26 +67,37 @@ fn find_sugg_for_if_let<'a, 'tcx>( arms: &[Arm<'_>], keyword: &'static str, ) { + fn find_suggestion(cx: &LateContext<'_, '_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { + if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { + return Some("is_ok()"); + } + if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { + return Some("is_err()"); + } + if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { + return Some("is_some()"); + } + if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { + return Some("is_none()"); + } + None + } + + let hir_id = expr.hir_id; let good_method = match arms[0].pat.kind { PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { - if match_qpath(path, &paths::RESULT_OK) { - "is_ok()" - } else if match_qpath(path, &paths::RESULT_ERR) { - "is_err()" - } else if match_qpath(path, &paths::OPTION_SOME) { - "is_some()" - } else { - return; - } + find_suggestion(cx, hir_id, path) } else { - return; + None } }, - - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", - - _ => return, + PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), + _ => None, + }; + let good_method = match good_method { + Some(method) => method, + None => return, }; // check that `while_let_on_iterator` lint does not trigger @@ -128,6 +142,7 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_ if arms.len() == 2 { let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + let hir_id = expr.hir_id; let found_good_method = match node_pair { ( PatKind::TupleStruct(ref path_left, ref patterns_left, _), @@ -142,6 +157,8 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_ &paths::RESULT_ERR, "is_ok()", "is_err()", + || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), + || can_suggest(cx, hir_id, sym!(result_type), "is_err"), ) } else { None @@ -160,6 +177,8 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_ &paths::OPTION_NONE, "is_some()", "is_none()", + || can_suggest(cx, hir_id, sym!(option_type), "is_some"), + || can_suggest(cx, hir_id, sym!(option_type), "is_none"), ) } else { None @@ -188,6 +207,7 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_ } } +#[allow(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( arms: &[Arm<'_>], path_left: &QPath<'_>, @@ -196,6 +216,8 @@ fn find_good_method_for_match<'a>( expected_right: &[&str], should_be_left: &'a str, should_be_right: &'a str, + can_suggest_left: impl Fn() -> bool, + can_suggest_right: impl Fn() -> bool, ) -> Option<&'a str> { let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { (&(*arms[0].body).kind, &(*arms[1].body).kind) @@ -207,10 +229,32 @@ fn find_good_method_for_match<'a>( match body_node_pair { (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { - (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), - (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), _ => None, }, _ => None, } } + +fn can_suggest(cx: &LateContext<'_, '_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { + if !in_constant(cx, hir_id) { + return true; + } + + // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. + cx.tcx + .get_diagnostic_item(diag_item) + .and_then(|def_id| { + cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .find_map(|item| match item.kind { + ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), + _ => None, + }) + }) + }) + .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) +} diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index fc8cb0e747c..6ba5cfb1d71 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![feature(const_if_match)] +#![feature(const_loop)] #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] @@ -67,6 +69,7 @@ fn main() { takes_bool(x); issue5504(); + issue5697(); let _ = if gen_opt().is_some() { 1 @@ -117,3 +120,42 @@ fn issue5504() { if m!().is_some() {} while m!().is_some() {} } + +// None of these should be linted because none of the suggested methods +// are `const fn` without toggling a feature. +const fn issue5697() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index 51912dade03..17de66f9ad0 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -1,5 +1,7 @@ // run-rustfix +#![feature(const_if_match)] +#![feature(const_loop)] #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] @@ -88,6 +90,7 @@ fn main() { takes_bool(x); issue5504(); + issue5697(); let _ = if let Some(_) = gen_opt() { 1 @@ -138,3 +141,42 @@ fn issue5504() { if let Some(_) = m!() {} while let Some(_) = m!() {} } + +// None of these should be linted because none of the suggested methods +// are `const fn` without toggling a feature. +const fn issue5697() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index b58deb7954e..1b9a4b40a2f 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:8:12 + --> $DIR/redundant_pattern_matching.rs:10:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` @@ -7,67 +7,67 @@ LL | if let Ok(_) = Ok::(42) {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:10:12 + --> $DIR/redundant_pattern_matching.rs:12:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:12:12 + --> $DIR/redundant_pattern_matching.rs:14:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:16:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:18:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:22:15 + --> $DIR/redundant_pattern_matching.rs:24:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:24:15 + --> $DIR/redundant_pattern_matching.rs:26:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:26:15 + --> $DIR/redundant_pattern_matching.rs:28:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:30:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:32:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:33:15 + --> $DIR/redundant_pattern_matching.rs:35:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 + --> $DIR/redundant_pattern_matching.rs:51:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:54:5 + --> $DIR/redundant_pattern_matching.rs:56:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +85,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:59:5 + --> $DIR/redundant_pattern_matching.rs:61:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:64:5 + --> $DIR/redundant_pattern_matching.rs:66:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +103,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:69:5 + --> $DIR/redundant_pattern_matching.rs:71:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:74:5 + --> $DIR/redundant_pattern_matching.rs:76:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:79:13 + --> $DIR/redundant_pattern_matching.rs:81:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,61 +131,61 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:84:20 + --> $DIR/redundant_pattern_matching.rs:86:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:87:20 + --> $DIR/redundant_pattern_matching.rs:89:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:92:20 + --> $DIR/redundant_pattern_matching.rs:95:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:94:19 + --> $DIR/redundant_pattern_matching.rs:97:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:96:19 + --> $DIR/redundant_pattern_matching.rs:99:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:98:19 + --> $DIR/redundant_pattern_matching.rs:101:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:131:19 + --> $DIR/redundant_pattern_matching.rs:134:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:132:16 + --> $DIR/redundant_pattern_matching.rs:135:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:138:12 + --> $DIR/redundant_pattern_matching.rs:141:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:15 + --> $DIR/redundant_pattern_matching.rs:142:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` diff --git a/tests/ui/redundant_pattern_matching_const_result.fixed b/tests/ui/redundant_pattern_matching_const_result.fixed new file mode 100644 index 00000000000..c8bc5458067 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_const_result.fixed @@ -0,0 +1,46 @@ +// run-rustfix + +#![feature(const_if_match)] +#![feature(const_loop)] +#![feature(const_result)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused)] + +// Test that results are linted with the feature enabled. + +const fn issue_5697() { + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + while Ok::(10).is_ok() {} + + while Ok::(10).is_err() {} + + Ok::(42).is_ok(); + + Err::(42).is_err(); + + // These should not be linted until `const_option` is implemented. + // See https://github.com/rust-lang/rust/issues/67441 + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} + +fn main() {} diff --git a/tests/ui/redundant_pattern_matching_const_result.rs b/tests/ui/redundant_pattern_matching_const_result.rs new file mode 100644 index 00000000000..75f37ec15c6 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_const_result.rs @@ -0,0 +1,52 @@ +// run-rustfix + +#![feature(const_if_match)] +#![feature(const_loop)] +#![feature(const_result)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused)] + +// Test that results are linted with the feature enabled. + +const fn issue_5697() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + + // These should not be linted until `const_option` is implemented. + // See https://github.com/rust-lang/rust/issues/67441 + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} + +fn main() {} diff --git a/tests/ui/redundant_pattern_matching_const_result.stderr b/tests/ui/redundant_pattern_matching_const_result.stderr new file mode 100644 index 00000000000..c32292f0eee --- /dev/null +++ b/tests/ui/redundant_pattern_matching_const_result.stderr @@ -0,0 +1,46 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_const_result.rs:12:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_const_result.rs:14:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_const_result.rs:16:15 + | +LL | while let Ok(_) = Ok::(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_const_result.rs:18:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_const_result.rs:20:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_const_result.rs:25:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: aborting due to 6 previous errors + From f3a40f5eb1cedebd374ebcd52341dfec60f3ea10 Mon Sep 17 00:00:00 2001 From: sozysozbot Date: Thu, 18 Jun 2020 06:50:04 +0900 Subject: [PATCH 0218/1110] Fix typo extending it's lifetime -> extending its lifetime --- clippy_lints/src/let_underscore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 710dec8d33f..acd628bbaca 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// **What it does:** Checks for `let _ = sync_lock` /// /// **Why is this bad?** This statement immediately drops the lock instead of - /// extending it's lifetime to the end of the scope, which is often not intended. + /// extending its lifetime to the end of the scope, which is often not intended. /// To extend lock lifetime to the end of the scope, use an underscore-prefixed /// name instead (i.e. _lock). If you want to explicitly drop the lock, /// `std::mem::drop` conveys your intention better and is less error-prone. From 51592f8587bd5dac9cb3f34fbd5896218a814677 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 23 Jun 2020 02:18:38 +0200 Subject: [PATCH 0219/1110] Fix sync fallout --- clippy_lints/src/escape.rs | 2 +- clippy_lints/src/len_zero.rs | 3 +- clippy_lints/src/loops.rs | 6 +-- clippy_lints/src/types.rs | 58 +++++-------------------- clippy_lints/src/unnecessary_sort_by.rs | 5 ++- clippy_lints/src/utils/author.rs | 5 ++- clippy_lints/src/utils/usage.rs | 2 +- 7 files changed, 27 insertions(+), 54 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 59af475af17..77e90eeac49 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::abi::LayoutOf; -use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase}; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use crate::utils::span_lint; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 13e85fda8ff..7838e8e8ab7 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -211,7 +211,8 @@ fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item<'_>, impl_items: &[Imp } fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) { + if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) + { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 83093ec51bd..ae1aa66be5c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase}; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -1580,13 +1580,13 @@ fn check_for_mutability(cx: &LateContext<'_, '_>, bound: &Expr<'_>) -> Option ( +fn check_for_mutation<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, body: &Expr<'_>, bound_ids: &[Option], ) -> (Option, Option) { let mut delegate = MutatePairDelegate { - cx: cx, + cx, hir_id_low: bound_ids[0], hir_id_high: bound_ids[1], span_low: None, diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index d59a2f1bae0..98de08f79f3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1945,16 +1945,12 @@ fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_ let which = match (&ty.kind, cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum, - (&ty::Int(ity), Constant::Int(i)) - if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => - { + (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => { Minimum }, (&ty::Bool, Constant::Bool(true)) => Maximum, - (&ty::Int(ity), Constant::Int(i)) - if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => - { + (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => { Maximum }, (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => Maximum, @@ -2083,50 +2079,20 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) } match pre_cast_ty.kind { ty::Int(int_ty) => Some(match int_ty { - IntTy::I8 => ( - FullInt::S(i128::from(i8::MIN)), - FullInt::S(i128::from(i8::MAX)), - ), - IntTy::I16 => ( - FullInt::S(i128::from(i16::MIN)), - FullInt::S(i128::from(i16::MAX)), - ), - IntTy::I32 => ( - FullInt::S(i128::from(i32::MIN)), - FullInt::S(i128::from(i32::MAX)), - ), - IntTy::I64 => ( - FullInt::S(i128::from(i64::MIN)), - FullInt::S(i128::from(i64::MAX)), - ), + IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))), + IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))), + IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))), + IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))), IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)), - IntTy::Isize => ( - FullInt::S(isize::MIN as i128), - FullInt::S(isize::MAX as i128), - ), + IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)), }), ty::Uint(uint_ty) => Some(match uint_ty { - UintTy::U8 => ( - FullInt::U(u128::from(u8::MIN)), - FullInt::U(u128::from(u8::MAX)), - ), - UintTy::U16 => ( - FullInt::U(u128::from(u16::MIN)), - FullInt::U(u128::from(u16::MAX)), - ), - UintTy::U32 => ( - FullInt::U(u128::from(u32::MIN)), - FullInt::U(u128::from(u32::MAX)), - ), - UintTy::U64 => ( - FullInt::U(u128::from(u64::MIN)), - FullInt::U(u128::from(u64::MAX)), - ), + UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))), + UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))), + UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))), + UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))), UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)), - UintTy::Usize => ( - FullInt::U(usize::MIN as u128), - FullInt::U(usize::MAX as u128), - ), + UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)), }), _ => None, } diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index e94eebb88e4..6ac6a12529c 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -95,7 +95,10 @@ fn mirrored_exprs( // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments - (ExprKind::MethodCall(left_segment, _, left_args, _), ExprKind::MethodCall(right_segment, _, right_args, _)) => { + ( + ExprKind::MethodCall(left_segment, _, left_args, _), + ExprKind::MethodCall(right_segment, _, right_args, _), + ) => { left_segment.ident == right_segment.ident && left_args .iter() diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 8b58bbb5e65..910b665ccb7 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -251,7 +251,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } }, ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => { - println!("MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", current); + println!( + "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", + current + ); println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); }, ExprKind::Tup(ref elements) => { diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 6a7a1f1ceaa..0492878fc27 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_span::symbol::{Ident, Symbol}; -use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase}; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &'a LateContext<'a, 'tcx>) -> Option> { From 52c486475774b7416e691323322ef1ea2db790de Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 11 Jun 2020 20:25:14 +0200 Subject: [PATCH 0220/1110] Improve end of expression check in for loop lints The code should to check that the current expression _is_ the end expression; not that it's equal to it. The equality check seems very wasteful in terms of performance. --- clippy_lints/src/loops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ae1aa66be5c..3874b040b13 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2042,7 +2042,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if self.state == VarState::DontWarn { return; } - if SpanlessEq::new(self.cx).eq_expr(&expr, self.end_expr) { + if expr.hir_id == self.end_expr.hir_id { self.past_loop = true; return; } From 51c3b42ef33f14cb40aa440d2d432e4c70e028b0 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 23 Jun 2020 14:27:11 +0700 Subject: [PATCH 0221/1110] Add more specific GitHub issue templates Apply suggestions from code review Co-authored-by: Philipp Krones --- .github/ISSUE_TEMPLATE.md | 8 ---- .github/ISSUE_TEMPLATE/blank_issue.md | 4 ++ .github/ISSUE_TEMPLATE/bug_report.md | 47 ++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 +++ .github/ISSUE_TEMPLATE/ice.md | 53 +++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/new_lint.md | 35 ++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 1 + 7 files changed, 145 insertions(+), 8 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/blank_issue.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/ice.md create mode 100644 .github/ISSUE_TEMPLATE/new_lint.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 15006a07b44..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md new file mode 100644 index 00000000000..9aef3ebe637 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/blank_issue.md @@ -0,0 +1,4 @@ +--- +name: Blank Issue +about: Create a blank issue. +--- diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..d8f0c44148c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,47 @@ +--- +name: Bug Report +about: Create a bug report for Clippy +labels: L-bug +--- + + +I tried this code: + +```rust + +``` + +I expected to see this happen: *explanation* + +Instead, this happened: *explanation* + +### Meta + +- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) +- `rustc -Vv`: + ``` + rustc 1.46.0-nightly (f455e46ea 2020-06-20) + binary: rustc + commit-hash: f455e46eae1a227d735091091144601b467e1565 + commit-date: 2020-06-20 + host: x86_64-unknown-linux-gnu + release: 1.46.0-nightly + LLVM version: 10.0 + ``` + + +

Backtrace +

+ + ``` + + ``` + +

+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..bd7dc0ac95c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Rust Programming Language Forum + url: https://users.rust-lang.org + about: Please ask and answer questions about Rust here. diff --git a/.github/ISSUE_TEMPLATE/ice.md b/.github/ISSUE_TEMPLATE/ice.md new file mode 100644 index 00000000000..3abe76bf2c4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ice.md @@ -0,0 +1,53 @@ +--- +name: Internal Compiler Error +about: Create a report for an internal compiler error in Clippy. +labels: L-bug, L-crash +--- + + +### Code + +```rust + +``` + +### Meta + +- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) +- `rustc -Vv`: + ``` + rustc 1.46.0-nightly (f455e46ea 2020-06-20) + binary: rustc + commit-hash: f455e46eae1a227d735091091144601b467e1565 + commit-date: 2020-06-20 + host: x86_64-unknown-linux-gnu + release: 1.46.0-nightly + LLVM version: 10.0 + ``` + +### Error output + +``` + +``` + + +
Backtrace +

+ + ``` + + ``` + +

+
diff --git a/.github/ISSUE_TEMPLATE/new_lint.md b/.github/ISSUE_TEMPLATE/new_lint.md new file mode 100644 index 00000000000..70445d7ef25 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new_lint.md @@ -0,0 +1,35 @@ +--- +name: New lint suggestion +about: Suggest a new Clippy lint. +labels: L-lint +--- + +### What it does + +*What does this lint do?* + +### Categories (optional) + +- Kind: *See for list of lint kinds* + +*What benefit of this lint over old code?* + +For example: +- Remove bounce checking inserted by ... +- Remove the need to duplicating/storing/typo ... + +### Drawbacks + +None. + +### Example + +```rust + +``` + +Could be written as: + +```rust + +``` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 97aa220afea..137a7363094 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -28,4 +28,5 @@ Delete this line and everything above before opening your PR. --- +*Please keep the line below* changelog: none From ed083cc95987ef37b7ed897f632b4a7a1baaff60 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 13 Jun 2020 18:22:38 +0200 Subject: [PATCH 0222/1110] Use lints in Clippy that are enabled in rustc bootstrap --- clippy_lints/src/lib.rs | 17 ++++++++++------- src/driver.rs | 6 +++++- src/main.rs | 2 ++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 021fbe932d8..25c1c2d73b6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,19 +1,22 @@ // error-pattern:cargo-clippy #![feature(bindings_after_at)] -#![feature(box_syntax)] #![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(concat_idents)] +#![feature(crate_visibility_modifier)] +#![feature(drain_filter)] #![feature(or_patterns)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] -#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)] #![recursion_limit = "512"] -#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)] -#![deny(rustc::internal)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] -#![feature(crate_visibility_modifier)] -#![feature(concat_idents)] -#![feature(drain_filter)] +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)] +#![warn(trivial_casts, trivial_numeric_casts)] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] +// warn on rustc internal lints +#![deny(rustc::internal)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) diff --git a/src/driver.rs b/src/driver.rs index 4453ae5ce44..10f56fb2070 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,5 +1,9 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(rustc_private)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] +// warn on rustc internal lints +#![deny(rustc::internal)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) diff --git a/src/main.rs b/src/main.rs index bc43a34ed5d..6739a4cf224 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] use rustc_tools_util::VersionInfo; use std::env; From b886c06c1a52a57890b318cc7304f7384e696ec8 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 13 Jun 2020 18:22:49 +0200 Subject: [PATCH 0223/1110] Fix fallout --- clippy_lints/src/loops.rs | 6 +++--- clippy_lints/src/suspicious_trait_impl.rs | 2 +- clippy_lints/src/trivially_copy_pass_by_ref.rs | 2 +- clippy_lints/src/utils/sugg.rs | 4 ++-- src/driver.rs | 6 ++++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ae1aa66be5c..9020b47a146 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1497,7 +1497,7 @@ struct MutatePairDelegate<'a, 'tcx> { span_high: Option, } -impl<'a, 'tcx> Delegate<'tcx> for MutatePairDelegate<'a, 'tcx> { +impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { @@ -1525,7 +1525,7 @@ impl<'a, 'tcx> Delegate<'tcx> for MutatePairDelegate<'a, 'tcx> { } } -impl<'a, 'tcx> MutatePairDelegate<'a, 'tcx> { +impl MutatePairDelegate<'_, '_> { fn mutation_span(&self) -> (Option, Option) { (self.span_low, self.span_high) } @@ -2292,7 +2292,7 @@ struct HasBreakOrReturnVisitor { has_break_or_return: bool, } -impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { +impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index a9e6fa329c0..cf71c3144a2 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -184,7 +184,7 @@ struct BinaryExprVisitor { in_binary_expr: bool, } -impl<'a, 'tcx> Visitor<'tcx> for BinaryExprVisitor { +impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 8e0cb94317a..146ac4b09d5 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -58,7 +58,7 @@ pub struct TriviallyCopyPassByRef { limit: u64, } -impl<'a, 'tcx> TriviallyCopyPassByRef { +impl<'tcx> TriviallyCopyPassByRef { pub fn new(limit: Option, target: &SessionConfig) -> Self { let limit = limit.unwrap_or_else(|| { let bit_width = u64::from(target.ptr_width); diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 73758b7eeb7..e919b1522d8 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -509,7 +509,7 @@ fn indentation(cx: &T, span: Span) -> Option { } /// Convenience extension trait for `DiagnosticBuilder`. -pub trait DiagnosticBuilderExt<'a, T: LintContext> { +pub trait DiagnosticBuilderExt { /// Suggests to add an attribute to an item. /// /// Correctly handles indentation of the attribute and item. @@ -556,7 +556,7 @@ pub trait DiagnosticBuilderExt<'a, T: LintContext> { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability); } -impl<'a, 'b, 'c, T: LintContext> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> { +impl DiagnosticBuilderExt for rustc_errors::DiagnosticBuilder<'_> { fn suggest_item_with_attr( &mut self, cx: &T, diff --git a/src/driver.rs b/src/driver.rs index 10f56fb2070..3d12436e9af 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -8,6 +8,8 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) #[allow(unused_extern_crates)] +extern crate rustc_data_structures; +#[allow(unused_extern_crates)] extern crate rustc_driver; #[allow(unused_extern_crates)] extern crate rustc_errors; @@ -97,7 +99,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { #[allow(clippy::find_map, clippy::filter_map)] fn describe_lints() { use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS}; - use std::collections::HashSet; + use rustc_data_structures::fx::FxHashSet; println!( " @@ -141,7 +143,7 @@ Available lint options: let scoped = |x: &str| format!("clippy::{}", x); - let lint_groups: HashSet<_> = lints.iter().map(|lint| lint.group).collect(); + let lint_groups: FxHashSet<_> = lints.iter().map(|lint| lint.group).collect(); println!("Lint checks provided by clippy:\n"); println!(" {} {:7.7} meaning", padded("name"), "default"); From 7374185b36d8dee6c91970a4c016d714a6e1bd39 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 23 Jun 2020 20:59:35 +0700 Subject: [PATCH 0224/1110] Remove unused allowed unused attributes --- clippy_lints/src/lib.rs | 32 +++----------------------------- src/driver.rs | 5 ----- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19ad7d92c2b..501220f28e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -20,47 +20,25 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) -#[allow(unused_extern_crates)] extern crate rustc_ast; -#[allow(unused_extern_crates)] extern crate rustc_ast_pretty; -#[allow(unused_extern_crates)] extern crate rustc_attr; -#[allow(unused_extern_crates)] extern crate rustc_data_structures; -#[allow(unused_extern_crates)] -extern crate rustc_driver; -#[allow(unused_extern_crates)] extern crate rustc_errors; -#[allow(unused_extern_crates)] extern crate rustc_hir; -#[allow(unused_extern_crates)] extern crate rustc_hir_pretty; -#[allow(unused_extern_crates)] extern crate rustc_index; -#[allow(unused_extern_crates)] extern crate rustc_infer; -#[allow(unused_extern_crates)] extern crate rustc_lexer; -#[allow(unused_extern_crates)] extern crate rustc_lint; -#[allow(unused_extern_crates)] extern crate rustc_middle; -#[allow(unused_extern_crates)] extern crate rustc_mir; -#[allow(unused_extern_crates)] extern crate rustc_parse; -#[allow(unused_extern_crates)] extern crate rustc_parse_format; -#[allow(unused_extern_crates)] extern crate rustc_session; -#[allow(unused_extern_crates)] extern crate rustc_span; -#[allow(unused_extern_crates)] extern crate rustc_target; -#[allow(unused_extern_crates)] extern crate rustc_trait_selection; -#[allow(unused_extern_crates)] extern crate rustc_typeck; use rustc_data_structures::fx::FxHashSet; @@ -85,14 +63,10 @@ use rustc_session::Session; /// # Example /// /// ``` -/// # #![feature(rustc_private)] -/// # #[allow(unused_extern_crates)] -/// # extern crate rustc_middle; -/// # #[allow(unused_extern_crates)] -/// # extern crate rustc_session; -/// # #[macro_use] -/// # use clippy_lints::declare_clippy_lint; +/// #![feature(rustc_private)] +/// extern crate rustc_session; /// use rustc_session::declare_tool_lint; +/// use clippy_lints::declare_clippy_lint; /// /// declare_clippy_lint! { /// /// **What it does:** Checks for ... (describe what the lint matches). diff --git a/src/driver.rs b/src/driver.rs index 3fca66a5792..decd3a79cce 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -7,15 +7,10 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) -#[allow(unused_extern_crates)] extern crate rustc_data_structures; -#[allow(unused_extern_crates)] extern crate rustc_driver; -#[allow(unused_extern_crates)] extern crate rustc_errors; -#[allow(unused_extern_crates)] extern crate rustc_interface; -#[allow(unused_extern_crates)] extern crate rustc_middle; use rustc_interface::interface; From fb4f9a0ad7a4656beb01c85b02b3e6ef15d914ec Mon Sep 17 00:00:00 2001 From: Teddy_Wang Date: Tue, 23 Jun 2020 11:40:38 -0400 Subject: [PATCH 0225/1110] Fix pattern match of ExprKind::MethodCall --- clippy_lints/src/map_identity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 9bc8f1bec1b..6607a26b130 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapIdentity { /// map(). Otherwise, returns None. fn get_map_argument<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind; + if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if args.len() == 2 && method.ident.as_str() == "map"; let caller_ty = cx.tables.expr_ty(&args[0]); if match_trait_method(cx, expr, &paths::ITERATOR) From 5987c7d4041ce5d72c8412d2ad73fe3b63308b51 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 9 Jun 2020 22:40:43 +0200 Subject: [PATCH 0226/1110] cmp_owned: avoid FP when PartialEq is not implemented symmetrically --- clippy_lints/src/misc.rs | 35 ++++++++++++---------- tests/ui/cmp_owned/issue_4874.rs | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 tests/ui/cmp_owned/issue_4874.rs diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index a0947608e60..1b65a01690d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -3,11 +3,11 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty, - TyKind, UnOp, + self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, + StmtKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; @@ -571,6 +571,15 @@ fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { } fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { + fn symmetric_partial_eq<'tcx>(cx: &LateContext<'_, 'tcx>, lhs: Ty<'tcx>, rhs: Ty<'tcx>) -> bool { + if let Some(trait_def_id) = cx.tcx.lang_items().eq_trait() { + return implements_trait(cx, lhs, trait_def_id, &[rhs.into()]) + && implements_trait(cx, rhs, trait_def_id, &[lhs.into()]); + } + + false + } + let (arg_ty, snip) = match expr.kind { ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => { if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) { @@ -594,18 +603,14 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { }; let other_ty = cx.tables.expr_ty_adjusted(other); - let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() { - Some(id) => id, - None => return, - }; - let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| { - implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()]) - }); - let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| { - implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()]) - }); - let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]); + let deref_arg_impl_partial_eq_other = arg_ty + .builtin_deref(true) + .map_or(false, |tam| symmetric_partial_eq(cx, tam.ty, other_ty)); + let arg_impl_partial_eq_deref_other = other_ty + .builtin_deref(true) + .map_or(false, |tam| symmetric_partial_eq(cx, arg_ty, tam.ty)); + let arg_impl_partial_eq_other = symmetric_partial_eq(cx, arg_ty, other_ty); if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other { return; @@ -694,7 +699,7 @@ fn non_macro_local(cx: &LateContext<'_, '_>, res: def::Res) -> bool { } } -fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { +fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { if_chain! { if let TyKind::Ptr(ref mut_ty) = ty.kind; if let ExprKind::Lit(ref lit) = e.kind; diff --git a/tests/ui/cmp_owned/issue_4874.rs b/tests/ui/cmp_owned/issue_4874.rs new file mode 100644 index 00000000000..b29c555eb1e --- /dev/null +++ b/tests/ui/cmp_owned/issue_4874.rs @@ -0,0 +1,51 @@ +#![allow(clippy::redundant_clone)] // See #5700 + +#[derive(PartialEq)] +struct Foo; + +struct Bar; + +impl std::fmt::Display for Bar { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "bar") + } +} + +// NOTE: PartialEq for T can't be implemented due to the orphan rules +impl PartialEq for Bar +where + T: AsRef + ?Sized, +{ + fn eq(&self, _: &T) -> bool { + true + } +} + +// NOTE: PartialEq for Foo is not implemented +impl PartialEq for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl ToOwned for Bar { + type Owned = Foo; + fn to_owned(&self) -> Foo { + Foo + } +} + +impl std::borrow::Borrow for Foo { + fn borrow(&self) -> &Bar { + static BAR: Bar = Bar; + &BAR + } +} + +fn main() { + let b = Bar {}; + if "Hi" == b.to_string() {} + + let f = Foo {}; + if f == b.to_owned() {} +} From b498e1d71537a79e7aff5378da625aca8b4eef96 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 13 Jun 2020 00:03:19 +0200 Subject: [PATCH 0227/1110] cmp_owned: reverse operands if necessary --- clippy_lints/src/misc.rs | 81 ++++++++++------ .../ui/cmp_owned/asymmetric_partial_eq.fixed | 93 +++++++++++++++++++ tests/ui/cmp_owned/asymmetric_partial_eq.rs | 93 +++++++++++++++++++ .../ui/cmp_owned/asymmetric_partial_eq.stderr | 46 +++++++++ tests/ui/cmp_owned/issue_4874.rs | 51 ---------- 5 files changed, 283 insertions(+), 81 deletions(-) create mode 100644 tests/ui/cmp_owned/asymmetric_partial_eq.fixed create mode 100644 tests/ui/cmp_owned/asymmetric_partial_eq.rs create mode 100644 tests/ui/cmp_owned/asymmetric_partial_eq.stderr delete mode 100644 tests/ui/cmp_owned/issue_4874.rs diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 1b65a01690d..76ffe6f6a1c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -371,8 +371,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints { if op.is_comparison() { check_nan(cx, left, expr); check_nan(cx, right, expr); - check_to_owned(cx, left, right); - check_to_owned(cx, right, left); + check_to_owned(cx, left, right, true); + check_to_owned(cx, right, left, false); } if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { if is_allowed(cx, left) || is_allowed(cx, right) { @@ -570,20 +570,30 @@ fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { matches!(&walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Array(_, _)) } -fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { - fn symmetric_partial_eq<'tcx>(cx: &LateContext<'_, 'tcx>, lhs: Ty<'tcx>, rhs: Ty<'tcx>) -> bool { - if let Some(trait_def_id) = cx.tcx.lang_items().eq_trait() { - return implements_trait(cx, lhs, trait_def_id, &[rhs.into()]) - && implements_trait(cx, rhs, trait_def_id, &[lhs.into()]); - } +fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { + #[derive(Default)] + struct EqImpl { + ty_eq_other: bool, + other_eq_ty: bool, + } - false + impl EqImpl { + fn is_implemented(&self) -> bool { + self.ty_eq_other || self.other_eq_ty + } + } + + fn symmetric_partial_eq<'tcx>(cx: &LateContext<'_, 'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option { + cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl { + ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]), + other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]), + }) } let (arg_ty, snip) = match expr.kind { ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => { if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) { - (cx.tables.expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, "..")) + (cx.tables.expr_ty(&args[0]), snippet(cx, args[0].span, "..")) } else { return; } @@ -591,7 +601,7 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { ExprKind::Call(ref path, ref v) if v.len() == 1 => { if let ExprKind::Path(ref path) = path.kind { if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { - (cx.tables.expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, "..")) + (cx.tables.expr_ty(&v[0]), snippet(cx, v[0].span, "..")) } else { return; } @@ -602,24 +612,19 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { _ => return, }; - let other_ty = cx.tables.expr_ty_adjusted(other); + let other_ty = cx.tables.expr_ty(other); - let deref_arg_impl_partial_eq_other = arg_ty + let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default(); + let with_deref = arg_ty .builtin_deref(true) - .map_or(false, |tam| symmetric_partial_eq(cx, tam.ty, other_ty)); - let arg_impl_partial_eq_deref_other = other_ty - .builtin_deref(true) - .map_or(false, |tam| symmetric_partial_eq(cx, arg_ty, tam.ty)); - let arg_impl_partial_eq_other = symmetric_partial_eq(cx, arg_ty, other_ty); + .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty)) + .unwrap_or_default(); - if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other { + if !with_deref.is_implemented() && !without_deref.is_implemented() { return; } - let other_gets_derefed = match other.kind { - ExprKind::Unary(UnOp::UnDeref, _) => true, - _ => false, - }; + let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::UnDeref, _)); let lint_span = if other_gets_derefed { expr.span.to(other.span) @@ -639,18 +644,34 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { return; } - let try_hint = if deref_arg_impl_partial_eq_other { - // suggest deref on the left - format!("*{}", snip) + let expr_snip; + let eq_impl; + if with_deref.is_implemented() { + expr_snip = format!("*{}", snip); + eq_impl = with_deref; } else { - // suggest dropping the to_owned on the left - snip.to_string() + expr_snip = snip.to_string(); + eq_impl = without_deref; }; + let span; + let hint; + if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) { + span = expr.span; + hint = expr_snip; + } else { + span = expr.span.to(other.span); + if eq_impl.ty_eq_other { + hint = format!("{} == {}", expr_snip, snippet(cx, other.span, "..")); + } else { + hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip); + } + } + diag.span_suggestion( - lint_span, + span, "try", - try_hint, + hint, Applicability::MachineApplicable, // snippet ); }, diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed new file mode 100644 index 00000000000..3305ac9bf8b --- /dev/null +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed @@ -0,0 +1,93 @@ +// run-rustfix +#![allow(unused, clippy::redundant_clone)] // See #5700 + +// Define the types in each module to avoid trait impls leaking between modules. +macro_rules! impl_types { + () => { + #[derive(PartialEq)] + pub struct Owned; + + pub struct Borrowed; + + impl ToOwned for Borrowed { + type Owned = Owned; + fn to_owned(&self) -> Owned { + Owned {} + } + } + + impl std::borrow::Borrow for Owned { + fn borrow(&self) -> &Borrowed { + static VALUE: Borrowed = Borrowed {}; + &VALUE + } + } + }; +} + +// Only Borrowed == Owned is implemented +mod borrowed_eq_owned { + impl_types!(); + + impl PartialEq for Borrowed { + fn eq(&self, _: &Owned) -> bool { + true + } + } + + pub fn compare() { + let owned = Owned {}; + let borrowed = Borrowed {}; + + if borrowed == owned {} + if borrowed == owned {} + } +} + +// Only Owned == Borrowed is implemented +mod owned_eq_borrowed { + impl_types!(); + + impl PartialEq for Owned { + fn eq(&self, _: &Borrowed) -> bool { + true + } + } + + fn compare() { + let owned = Owned {}; + let borrowed = Borrowed {}; + + if owned == borrowed {} + if owned == borrowed {} + } +} + +mod issue_4874 { + impl_types!(); + + // NOTE: PartialEq for T can't be implemented due to the orphan rules + impl PartialEq for Borrowed + where + T: AsRef + ?Sized, + { + fn eq(&self, _: &T) -> bool { + true + } + } + + impl std::fmt::Display for Borrowed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "borrowed") + } + } + + fn compare() { + let borrowed = Borrowed {}; + + if borrowed == "Hi" {} + if borrowed == "Hi" {} + } +} + +fn main() {} diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/tests/ui/cmp_owned/asymmetric_partial_eq.rs new file mode 100644 index 00000000000..88bc2f51dd6 --- /dev/null +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.rs @@ -0,0 +1,93 @@ +// run-rustfix +#![allow(unused, clippy::redundant_clone)] // See #5700 + +// Define the types in each module to avoid trait impls leaking between modules. +macro_rules! impl_types { + () => { + #[derive(PartialEq)] + pub struct Owned; + + pub struct Borrowed; + + impl ToOwned for Borrowed { + type Owned = Owned; + fn to_owned(&self) -> Owned { + Owned {} + } + } + + impl std::borrow::Borrow for Owned { + fn borrow(&self) -> &Borrowed { + static VALUE: Borrowed = Borrowed {}; + &VALUE + } + } + }; +} + +// Only Borrowed == Owned is implemented +mod borrowed_eq_owned { + impl_types!(); + + impl PartialEq for Borrowed { + fn eq(&self, _: &Owned) -> bool { + true + } + } + + pub fn compare() { + let owned = Owned {}; + let borrowed = Borrowed {}; + + if borrowed.to_owned() == owned {} + if owned == borrowed.to_owned() {} + } +} + +// Only Owned == Borrowed is implemented +mod owned_eq_borrowed { + impl_types!(); + + impl PartialEq for Owned { + fn eq(&self, _: &Borrowed) -> bool { + true + } + } + + fn compare() { + let owned = Owned {}; + let borrowed = Borrowed {}; + + if owned == borrowed.to_owned() {} + if borrowed.to_owned() == owned {} + } +} + +mod issue_4874 { + impl_types!(); + + // NOTE: PartialEq for T can't be implemented due to the orphan rules + impl PartialEq for Borrowed + where + T: AsRef + ?Sized, + { + fn eq(&self, _: &T) -> bool { + true + } + } + + impl std::fmt::Display for Borrowed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "borrowed") + } + } + + fn compare() { + let borrowed = Borrowed {}; + + if "Hi" == borrowed.to_string() {} + if borrowed.to_string() == "Hi" {} + } +} + +fn main() {} diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.stderr b/tests/ui/cmp_owned/asymmetric_partial_eq.stderr new file mode 100644 index 00000000000..43bf8851fc6 --- /dev/null +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.stderr @@ -0,0 +1,46 @@ +error: this creates an owned instance just for comparison + --> $DIR/asymmetric_partial_eq.rs:42:12 + | +LL | if borrowed.to_owned() == owned {} + | ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed` + | + = note: `-D clippy::cmp-owned` implied by `-D warnings` + +error: this creates an owned instance just for comparison + --> $DIR/asymmetric_partial_eq.rs:43:21 + | +LL | if owned == borrowed.to_owned() {} + | ---------^^^^^^^^^^^^^^^^^^^ + | | + | help: try: `borrowed == owned` + +error: this creates an owned instance just for comparison + --> $DIR/asymmetric_partial_eq.rs:61:21 + | +LL | if owned == borrowed.to_owned() {} + | ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed` + +error: this creates an owned instance just for comparison + --> $DIR/asymmetric_partial_eq.rs:62:12 + | +LL | if borrowed.to_owned() == owned {} + | ^^^^^^^^^^^^^^^^^^^--------- + | | + | help: try: `owned == borrowed` + +error: this creates an owned instance just for comparison + --> $DIR/asymmetric_partial_eq.rs:88:20 + | +LL | if "Hi" == borrowed.to_string() {} + | --------^^^^^^^^^^^^^^^^^^^^ + | | + | help: try: `borrowed == "Hi"` + +error: this creates an owned instance just for comparison + --> $DIR/asymmetric_partial_eq.rs:89:12 + | +LL | if borrowed.to_string() == "Hi" {} + | ^^^^^^^^^^^^^^^^^^^^ help: try: `borrowed` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/cmp_owned/issue_4874.rs b/tests/ui/cmp_owned/issue_4874.rs deleted file mode 100644 index b29c555eb1e..00000000000 --- a/tests/ui/cmp_owned/issue_4874.rs +++ /dev/null @@ -1,51 +0,0 @@ -#![allow(clippy::redundant_clone)] // See #5700 - -#[derive(PartialEq)] -struct Foo; - -struct Bar; - -impl std::fmt::Display for Bar { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "bar") - } -} - -// NOTE: PartialEq for T can't be implemented due to the orphan rules -impl PartialEq for Bar -where - T: AsRef + ?Sized, -{ - fn eq(&self, _: &T) -> bool { - true - } -} - -// NOTE: PartialEq for Foo is not implemented -impl PartialEq for Bar { - fn eq(&self, _: &Foo) -> bool { - true - } -} - -impl ToOwned for Bar { - type Owned = Foo; - fn to_owned(&self) -> Foo { - Foo - } -} - -impl std::borrow::Borrow for Foo { - fn borrow(&self) -> &Bar { - static BAR: Bar = Bar; - &BAR - } -} - -fn main() { - let b = Bar {}; - if "Hi" == b.to_string() {} - - let f = Foo {}; - if f == b.to_owned() {} -} From 6bf5434e19ce6d2a501589d1fcbc0d1748c531a6 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 24 Jun 2020 01:01:23 +0200 Subject: [PATCH 0228/1110] copy_on_clone - add machine applicability --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/clone_on_copy.fixed | 40 +++++++++++++++++++++ tests/ui/clone_on_copy.rs | 40 +++++++++++++++++++++ tests/ui/clone_on_copy.stderr | 34 ++++++++++++++++++ tests/ui/unnecessary_clone.rs | 25 ------------- tests/ui/unnecessary_clone.stderr | 58 ++++++++----------------------- 6 files changed, 129 insertions(+), 70 deletions(-) create mode 100644 tests/ui/clone_on_copy.fixed create mode 100644 tests/ui/clone_on_copy.rs create mode 100644 tests/ui/clone_on_copy.stderr diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 214cf0c130f..74fefa65612 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2041,7 +2041,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir: } span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::Unspecified); + diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); } }); } diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed new file mode 100644 index 00000000000..1f0ca101757 --- /dev/null +++ b/tests/ui/clone_on_copy.fixed @@ -0,0 +1,40 @@ +// run-rustfix + +#![allow( + unused, + clippy::redundant_clone, + clippy::deref_addrof, + clippy::no_effect, + clippy::unnecessary_operation +)] + +use std::cell::RefCell; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +fn is_ascii(ch: char) -> bool { + ch.is_ascii() +} + +fn clone_on_copy() { + 42; + + vec![1].clone(); // ok, not a Copy type + Some(vec![1]).clone(); // ok, not a Copy type + *(&42); + + let rc = RefCell::new(0); + *rc.borrow(); + + // Issue #4348 + let mut x = 43; + let _ = &x.clone(); // ok, getting a ref + 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate + is_ascii('z'); + + // Issue #5436 + let mut vec = Vec::new(); + vec.push(42); +} diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs new file mode 100644 index 00000000000..ca39a654b4f --- /dev/null +++ b/tests/ui/clone_on_copy.rs @@ -0,0 +1,40 @@ +// run-rustfix + +#![allow( + unused, + clippy::redundant_clone, + clippy::deref_addrof, + clippy::no_effect, + clippy::unnecessary_operation +)] + +use std::cell::RefCell; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +fn is_ascii(ch: char) -> bool { + ch.is_ascii() +} + +fn clone_on_copy() { + 42.clone(); + + vec![1].clone(); // ok, not a Copy type + Some(vec![1]).clone(); // ok, not a Copy type + (&42).clone(); + + let rc = RefCell::new(0); + rc.borrow().clone(); + + // Issue #4348 + let mut x = 43; + let _ = &x.clone(); // ok, getting a ref + 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate + is_ascii('z'.clone()); + + // Issue #5436 + let mut vec = Vec::new(); + vec.push(42.clone()); +} diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr new file mode 100644 index 00000000000..ec2faf4ab40 --- /dev/null +++ b/tests/ui/clone_on_copy.stderr @@ -0,0 +1,34 @@ +error: using `clone` on a `Copy` type + --> $DIR/clone_on_copy.rs:22:5 + | +LL | 42.clone(); + | ^^^^^^^^^^ help: try removing the `clone` call: `42` + | + = note: `-D clippy::clone-on-copy` implied by `-D warnings` + +error: using `clone` on a `Copy` type + --> $DIR/clone_on_copy.rs:26:5 + | +LL | (&42).clone(); + | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` + +error: using `clone` on a `Copy` type + --> $DIR/clone_on_copy.rs:29:5 + | +LL | rc.borrow().clone(); + | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` + +error: using `clone` on a `Copy` type + --> $DIR/clone_on_copy.rs:35:14 + | +LL | is_ascii('z'.clone()); + | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` + +error: using `clone` on a `Copy` type + --> $DIR/clone_on_copy.rs:39:14 + | +LL | vec.push(42.clone()); + | ^^^^^^^^^^ help: try removing the `clone` call: `42` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index f1cc5b564c1..2c9d4d39e6c 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -13,31 +13,6 @@ impl SomeTrait for SomeImpl {} fn main() {} -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() { - 42.clone(); - - vec![1].clone(); // ok, not a Copy type - Some(vec![1]).clone(); // ok, not a Copy type - (&42).clone(); - - let rc = RefCell::new(0); - rc.borrow().clone(); - - // Issue #4348 - let mut x = 43; - let _ = &x.clone(); // ok, getting a ref - 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate - is_ascii('z'.clone()); - - // Issue #5436 - let mut vec = Vec::new(); - vec.push(42.clone()); -} - fn clone_on_ref_ptr() { let rc = Rc::new(true); let arc = Arc::new(true); diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 6176a2bc464..113fab69009 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -1,37 +1,5 @@ -error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:21:5 - | -LL | 42.clone(); - | ^^^^^^^^^^ help: try removing the `clone` call: `42` - | - = note: `-D clippy::clone-on-copy` implied by `-D warnings` - -error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:25:5 - | -LL | (&42).clone(); - | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` - -error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:28:5 - | -LL | rc.borrow().clone(); - | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` - -error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:34:14 - | -LL | is_ascii('z'.clone()); - | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` - -error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:38:14 - | -LL | vec.push(42.clone()); - | ^^^^^^^^^^ help: try removing the `clone` call: `42` - error: using `.clone()` on a ref-counted pointer - --> $DIR/unnecessary_clone.rs:48:5 + --> $DIR/unnecessary_clone.rs:23:5 | LL | rc.clone(); | ^^^^^^^^^^ help: try this: `Rc::::clone(&rc)` @@ -39,43 +7,45 @@ LL | rc.clone(); = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` error: using `.clone()` on a ref-counted pointer - --> $DIR/unnecessary_clone.rs:51:5 + --> $DIR/unnecessary_clone.rs:26:5 | LL | arc.clone(); | ^^^^^^^^^^^ help: try this: `Arc::::clone(&arc)` error: using `.clone()` on a ref-counted pointer - --> $DIR/unnecessary_clone.rs:54:5 + --> $DIR/unnecessary_clone.rs:29:5 | LL | rcweak.clone(); | ^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&rcweak)` error: using `.clone()` on a ref-counted pointer - --> $DIR/unnecessary_clone.rs:57:5 + --> $DIR/unnecessary_clone.rs:32:5 | LL | arc_weak.clone(); | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` error: using `.clone()` on a ref-counted pointer - --> $DIR/unnecessary_clone.rs:61:33 + --> $DIR/unnecessary_clone.rs:36:33 | LL | let _: Arc = x.clone(); | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:65:5 + --> $DIR/unnecessary_clone.rs:40:5 | LL | t.clone(); | ^^^^^^^^^ help: try removing the `clone` call: `t` + | + = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:67:5 + --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type - --> $DIR/unnecessary_clone.rs:73:22 + --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); | ^^^^^^^^^ @@ -91,13 +61,13 @@ LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `clone` on a `Copy` type - --> $DIR/unnecessary_clone.rs:109:20 + --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); | ^^^^^^^^^ help: try dereferencing it: `*****a` error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type - --> $DIR/unnecessary_clone.rs:114:22 + --> $DIR/unnecessary_clone.rs:89:22 | LL | let _ = &mut encoded.clone(); | ^^^^^^^^^^^^^^^ @@ -112,7 +82,7 @@ LL | let _ = &mut <&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type - --> $DIR/unnecessary_clone.rs:115:18 + --> $DIR/unnecessary_clone.rs:90:18 | LL | let _ = &encoded.clone(); | ^^^^^^^^^^^^^^^ @@ -126,5 +96,5 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &<&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 11 previous errors From fa0f1d3e5353df04e3d2e68e65810ca1a231c220 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Thu, 25 Jun 2020 11:25:21 -0400 Subject: [PATCH 0229/1110] Change a noun to a verb to make the sentence complete --- clippy_lints/src/await_holding_lock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index a88f922d8e0..8f7e06b32ff 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// non-async-aware MutexGuard. /// /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot - /// are not designed to operator in an async context across await points. + /// are not designed to operate in an async context across await points. /// /// There are two potential solutions. One is to use an asynx-aware Mutex /// type. Many asynchronous foundation crates provide such a Mutex type. The From ab649c920e18929c8f2b3399178bcfd1ecdcdd3e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 30 Jun 2020 16:19:42 +0200 Subject: [PATCH 0230/1110] Disable chrono integration test --- .github/workflows/clippy_bors.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 0c80394f03e..fd0cd7a1890 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -240,7 +240,8 @@ jobs: - 'Geal/nom' - 'rust-lang/stdarch' - 'serde-rs/serde' - - 'chronotope/chrono' + # FIXME: chrono currently cannot be compiled with `--all-targets` + # - 'chronotope/chrono' - 'hyperium/hyper' - 'rust-random/rand' - 'rust-lang/futures-rs' From 814349f9418b3d247e2e3cc952877e510f824fdd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 26 Jun 2020 10:52:14 +0200 Subject: [PATCH 0231/1110] Lint enabling the whole restriction group --- CHANGELOG.md | 1 + clippy_lints/src/attrs.rs | 95 +++++++++++++++++++++++++++------------ clippy_lints/src/lib.rs | 3 ++ src/lintlist/mod.rs | 7 +++ tests/ui/attrs.rs | 6 +++ tests/ui/attrs.stderr | 33 ++++++++++++-- 6 files changed, 113 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6186c319d0..b88044d6ce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1352,6 +1352,7 @@ Released 2018-09-13 [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 41f125d4839..dd4dffda0f9 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -2,8 +2,8 @@ use crate::reexport::Name; use crate::utils::{ - first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, without_block_comments, + first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help, + span_lint_and_sugg, span_lint_and_then, without_block_comments, }; use if_chain::if_chain; use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; @@ -17,7 +17,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Symbol, SymbolStr}; use semver::Version; static UNIX_SYSTEMS: &[&str] = &[ @@ -182,6 +182,29 @@ declare_clippy_lint! { "unknown_lints for scoped Clippy lints" } +declare_clippy_lint! { + /// **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. + /// + /// **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. + /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// #![deny(clippy::restriction)] + /// ``` + /// + /// Good: + /// ```rust + /// #![deny(clippy::as_conversions)] + /// ``` + pub BLANKET_CLIPPY_RESTRICTION_LINTS, + correctness, + "enabling the complete restriction group" +} + declare_clippy_lint! { /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it /// with `#[rustfmt::skip]`. @@ -249,15 +272,17 @@ declare_lint_pass!(Attributes => [ DEPRECATED_SEMVER, USELESS_ATTRIBUTE, UNKNOWN_CLIPPY_LINTS, + BLANKET_CLIPPY_RESTRICTION_LINTS, ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes { fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) { if let Some(items) = &attr.meta_item_list() { if let Some(ident) = attr.ident() { - match &*ident.as_str() { + let ident = &*ident.as_str(); + match ident { "allow" | "warn" | "deny" | "forbid" => { - check_clippy_lint_names(cx, items); + check_clippy_lint_names(cx, ident, items); }, _ => {}, } @@ -363,38 +388,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes { } } -#[allow(clippy::single_match_else)] -fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) { - let lint_store = cx.lints(); - for lint in items { +fn check_clippy_lint_names(cx: &LateContext<'_, '_>, ident: &str, items: &[NestedMetaItem]) { + fn extract_name(lint: &NestedMetaItem) -> Option { if_chain! { if let Some(meta_item) = lint.meta_item(); if meta_item.path.segments.len() > 1; if let tool_name = meta_item.path.segments[0].ident; if tool_name.as_str() == "clippy"; - let name = meta_item.path.segments.last().unwrap().ident.name; - if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name( - &name.as_str(), - Some(tool_name.name), - ); + let lint_name = meta_item.path.segments.last().unwrap().ident.name; then { + return Some(lint_name.as_str()); + } + } + None + } + + let lint_store = cx.lints(); + for lint in items { + if let Some(lint_name) = extract_name(lint) { + if let CheckLintNameResult::Tool(Err((None, _))) = + lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) + { span_lint_and_then( cx, UNKNOWN_CLIPPY_LINTS, lint.span(), - &format!("unknown clippy lint: clippy::{}", name), + &format!("unknown clippy lint: clippy::{}", lint_name), |diag| { - let name_lower = name.as_str().to_lowercase(); - let symbols = lint_store.get_lints().iter().map( - |l| Symbol::intern(&l.name_lower()) - ).collect::>(); - let sugg = find_best_match_for_name( - symbols.iter(), - &format!("clippy::{}", name_lower), - None, - ); - if name.as_str().chars().any(char::is_uppercase) - && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() { + let name_lower = lint_name.to_lowercase(); + let symbols = lint_store + .get_lints() + .iter() + .map(|l| Symbol::intern(&l.name_lower())) + .collect::>(); + let sugg = find_best_match_for_name(symbols.iter(), &format!("clippy::{}", name_lower), None); + if lint_name.chars().any(char::is_uppercase) + && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() + { diag.span_suggestion( lint.span(), "lowercase the lint name", @@ -409,10 +439,19 @@ fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) { Applicability::MachineApplicable, ); } - } + }, + ); + } else if lint_name == "restriction" && ident != "allow" { + span_lint_and_help( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + lint.span(), + "restriction lints are not meant to be all enabled", + None, + "try enabling only the lints you really need", ); } - }; + } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 756b6b9a8a4..24eb492ee72 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -474,6 +474,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, &atomic_ordering::INVALID_ATOMIC_ORDERING, + &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, &attrs::DEPRECATED_CFG_ATTR, &attrs::DEPRECATED_SEMVER, &attrs::EMPTY_LINE_AFTER_OUTER_ATTR, @@ -1189,6 +1190,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), + LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_CFG_ATTR), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), @@ -1614,6 +1616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), + LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5a43a1a07d2..8d27e6282f1 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -80,6 +80,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "blacklisted_name", }, + Lint { + name: "blanket_clippy_restriction_lints", + group: "correctness", + desc: "enabling the complete restriction group", + deprecation: None, + module: "attrs", + }, Lint { name: "blocks_in_if_conditions", group: "style", diff --git a/tests/ui/attrs.rs b/tests/ui/attrs.rs index 91b65a43be7..908d063729f 100644 --- a/tests/ui/attrs.rs +++ b/tests/ui/attrs.rs @@ -1,5 +1,11 @@ #![warn(clippy::inline_always, clippy::deprecated_semver)] #![allow(clippy::assertions_on_constants)] +// Test that the whole restriction group is not enabled +#![warn(clippy::restriction)] +#![deny(clippy::restriction)] +#![forbid(clippy::restriction)] +#![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)] + #[inline(always)] fn test_attr_lint() { assert!(true) diff --git a/tests/ui/attrs.stderr b/tests/ui/attrs.stderr index 39ddf6f226d..adceb4b6369 100644 --- a/tests/ui/attrs.stderr +++ b/tests/ui/attrs.stderr @@ -1,5 +1,5 @@ error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea - --> $DIR/attrs.rs:3:1 + --> $DIR/attrs.rs:9:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[inline(always)] = note: `-D clippy::inline-always` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:23:14 + --> $DIR/attrs.rs:29:14 | LL | #[deprecated(since = "forever")] | ^^^^^^^^^^^^^^^^^ @@ -15,10 +15,35 @@ LL | #[deprecated(since = "forever")] = note: `-D clippy::deprecated-semver` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:26:14 + --> $DIR/attrs.rs:32:14 | LL | #[deprecated(since = "1")] | ^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: restriction lints are not meant to be all enabled + --> $DIR/attrs.rs:4:9 + | +LL | #![warn(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::blanket_clippy_restriction_lints)]` on by default + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/attrs.rs:5:9 + | +LL | #![deny(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/attrs.rs:6:11 + | +LL | #![forbid(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: aborting due to 6 previous errors From c5d8f530e0625f14c5b4bedebdf0dc53064310c9 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 27 Jun 2020 21:49:34 +0200 Subject: [PATCH 0232/1110] Move blanket_clippy_restriction_lints to "style" --- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/lib.rs | 2 +- src/lintlist/mod.rs | 2 +- tests/ui/attrs.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index dd4dffda0f9..bb9d8be5dae 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -201,7 +201,7 @@ declare_clippy_lint! { /// #![deny(clippy::as_conversions)] /// ``` pub BLANKET_CLIPPY_RESTRICTION_LINTS, - correctness, + style, "enabling the complete restriction group" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 24eb492ee72..50116a95612 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1443,6 +1443,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assign_ops::ASSIGN_OP_PATTERN), + LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), @@ -1616,7 +1617,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), - LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8d27e6282f1..5119fb40337 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -82,7 +82,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "blanket_clippy_restriction_lints", - group: "correctness", + group: "style", desc: "enabling the complete restriction group", deprecation: None, module: "attrs", diff --git a/tests/ui/attrs.stderr b/tests/ui/attrs.stderr index adceb4b6369..ef4b89eaa6d 100644 --- a/tests/ui/attrs.stderr +++ b/tests/ui/attrs.stderr @@ -26,7 +26,7 @@ error: restriction lints are not meant to be all enabled LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::blanket_clippy_restriction_lints)]` on by default + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` = help: try enabling only the lints you really need error: restriction lints are not meant to be all enabled From bff6c435ef597071410af5d136916231f310c64c Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 1 Jul 2020 00:15:21 +0200 Subject: [PATCH 0233/1110] Require `or_patterns` to suggest nesting them --- clippy_lints/src/unnested_or_patterns.rs | 4 ++-- tests/ui/unnested_or_patterns3.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 tests/ui/unnested_or_patterns3.rs diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 4d3682263f1..169a486d1eb 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -72,8 +72,8 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { - if !cx.sess.opts.unstable_features.is_nightly_build() { - // User cannot do `#![feature(or_patterns)]`, so bail. + if !cx.sess.features_untracked().or_patterns { + // Do not suggest nesting the patterns if the feature `or_patterns` is not enabled. return; } diff --git a/tests/ui/unnested_or_patterns3.rs b/tests/ui/unnested_or_patterns3.rs new file mode 100644 index 00000000000..6bd35057bfa --- /dev/null +++ b/tests/ui/unnested_or_patterns3.rs @@ -0,0 +1,6 @@ +#![warn(clippy::unnested_or_patterns)] + +// Test that `unnested_or_patterns` does not trigger without enabling `or_patterns` +fn main() { + if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} +} From 5b9c2ff9ccebbc9ddb646c6f868f9972ccc64f4f Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Wed, 1 Jul 2020 07:30:03 +0200 Subject: [PATCH 0234/1110] Fix multiple_crate_versions error Fix the versions of packages in the multiple_crate_versions ui test by checking in the Cargo.lock for the test package. `ansi_term 0.11` depends on `winapi ^0.3.4`. This means means that the expected stderr for this test would have to be updated whenever `winapi 0.3` is updated otherwise. --- .../multiple_crate_versions/fail/Cargo.lock | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock new file mode 100644 index 00000000000..7e96aa36feb --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock @@ -0,0 +1,109 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "ctrlc" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c" +dependencies = [ + "kernel32-sys", + "nix", + "winapi 0.2.8", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "multiple_crate_versions" +version = "0.1.0" +dependencies = [ + "ansi_term", + "ctrlc", +] + +[[package]] +name = "nix" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "void", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" From d347d0cf59d0a502361db873d900f4587c6e34de Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Wed, 1 Jul 2020 12:36:36 +0200 Subject: [PATCH 0235/1110] Deprecate regex_macro lint --- clippy_lints/src/deprecated_lints.rs | 10 ++++- clippy_lints/src/lib.rs | 9 +++-- clippy_lints/src/regex.rs | 57 ++-------------------------- clippy_lints/src/utils/paths.rs | 1 - src/lintlist/mod.rs | 7 ---- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +++- tests/ui/regex.rs | 2 +- 8 files changed, 26 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 6e8ca647dd7..818d8188a78 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -153,5 +153,13 @@ declare_deprecated_lint! { /// /// **Deprecation reason:** Associated-constants are now preferred. pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants" + "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The regex! macro does not exist anymore. + pub REGEX_MACRO, + "the regex! macro has been removed from the regex crate in 2018" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 50116a95612..d31a597b66a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -460,7 +460,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_removed( "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants", + "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants", + ); + store.register_removed( + "clippy::regex_macro", + "the regex! macro has been removed from the regex crate in 2018", ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` @@ -755,7 +759,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &reference::DEREF_ADDROF, &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, - ®ex::REGEX_MACRO, ®ex::TRIVIAL_REGEX, &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, @@ -1380,7 +1383,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), - LintId::of(®ex::REGEX_MACRO), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), @@ -1517,7 +1519,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(®ex::REGEX_MACRO), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 9c54c3cbac0..7405edbcd19 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,9 +1,9 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help}; +use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{BytePos, Span}; @@ -46,66 +46,15 @@ declare_clippy_lint! { "trivial regular expressions" } -declare_clippy_lint! { - /// **What it does:** Checks for usage of `regex!(_)` which (as of now) is - /// usually slower than `Regex::new(_)` unless called in a loop (which is a bad - /// idea anyway). - /// - /// **Why is this bad?** Performance, at least for now. The macro version is - /// likely to catch up long-term, but for now the dynamic version is faster. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```ignore - /// regex!("foo|bar") - /// ``` - pub REGEX_MACRO, - style, - "use of `regex!(_)` instead of `Regex::new(_)`" -} - #[derive(Clone, Default)] pub struct Regex { spans: FxHashSet, last: Option, } -impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]); +impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex { - fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) { - self.spans.clear(); - } - - fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { - if_chain! { - if self.last.is_none(); - if let Some(ref expr) = block.expr; - if match_type(cx, cx.tables().expr_ty(expr), &paths::REGEX); - if let Some(span) = is_expn_of(expr.span, "regex"); - then { - if !self.spans.contains(&span) { - span_lint( - cx, - REGEX_MACRO, - span, - "`regex!(_)` found. \ - Please use `Regex::new(_)`, which is faster for now." - ); - self.spans.insert(span); - } - self.last = Some(block.hir_id); - } - } - } - - fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { - if self.last.map_or(false, |id| block.hir_id == id) { - self.last = None; - } - } - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if let ExprKind::Call(ref fun, ref args) = expr.kind; diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 3b7e9739211..4c3462802e9 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -98,7 +98,6 @@ pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; -pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5119fb40337..a2998d74130 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1865,13 +1865,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "reference", }, - Lint { - name: "regex_macro", - group: "style", - desc: "use of `regex!(_)` instead of `Regex::new(_)`", - deprecation: None, - module: "regex", - }, Lint { name: "rest_pat_in_fully_bound_structs", group: "restriction", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 188a641aa1a..3eefb232780 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -7,5 +7,6 @@ #[warn(clippy::invalid_ref)] #[warn(clippy::into_iter_on_array)] #[warn(clippy::unused_label)] +#[warn(clippy::regex_macro)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index a4efe3d15a9..a80e2bf31fe 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -54,11 +54,17 @@ error: lint `clippy::unused_label` has been removed: `this lint has been uplifte LL | #[warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018` + --> $DIR/deprecated.rs:10:8 + | +LL | #[warn(clippy::regex_macro)] + | ^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index b523fa5b711..9767e5bf76a 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -1,5 +1,5 @@ #![allow(unused)] -#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_macro)] +#![warn(clippy::invalid_regex, clippy::trivial_regex)] extern crate regex; From 754bfb1dc89ed9a98b2f1b7d77b035e809b14031 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 28 Jun 2020 12:14:04 +0200 Subject: [PATCH 0236/1110] Add configurable threshold for `type_repetition_in_bounds` lint --- clippy_lints/src/lib.rs | 5 +++-- clippy_lints/src/trait_bounds.rs | 18 +++++++++++++--- clippy_lints/src/utils/conf.rs | 2 ++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/type_repetition_in_bounds.rs | 21 ++++++++++++++++++- tests/ui/type_repetition_in_bounds.stderr | 18 +++++++++++----- 6 files changed, 54 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d31a597b66a..38f8d007c72 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -996,7 +996,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box checked_conversions::CheckedConversions); store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box inherent_to_string::InherentToString); - store.register_late_pass(|| box trait_bounds::TraitBounds); + let max_trait_bounds = conf.max_trait_bounds; + store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds)); store.register_late_pass(|| box comparison_chain::ComparisonChain); store.register_late_pass(|| box mut_key::MutableKeyType); store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic); @@ -1033,7 +1034,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let array_size_threshold = conf.array_size_threshold; store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold)); store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); - store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic); + store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 9eb2079c3ca..650edbb4b11 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -5,9 +5,6 @@ use rustc_hir::{GenericBound, Generics, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -#[derive(Copy, Clone)] -pub struct TraitBounds; - declare_clippy_lint! { /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds /// @@ -29,6 +26,18 @@ declare_clippy_lint! { "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } +#[derive(Copy, Clone)] +pub struct TraitBounds { + max_trait_bounds: u64, +} + +impl TraitBounds { + #[must_use] + pub fn new(max_trait_bounds: u64) -> Self { + Self { max_trait_bounds } + } +} + impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { @@ -45,6 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { if let WherePredicate::BoundPredicate(ref p) = bound { + if p.bounds.len() as u64 > self.max_trait_bounds { + return; + } let h = hash(&p.bounded_ty); if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()) { let mut hint_string = format!( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c41befbf147..de425211e38 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -156,6 +156,8 @@ define_Conf! { (array_size_threshold, "array_size_threshold": u64, 512_000), /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096), + /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted + (max_trait_bounds, "max_trait_bounds": u64, 3), /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have (max_struct_bools, "max_struct_bools": u64, 3), /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 53970af4107..6fbba01416a 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -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`, `warn-on-all-wildcard-imports`, `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-trait-bounds`, `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 diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 8b538be762b..60b994548b5 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -1,4 +1,6 @@ -#[deny(clippy::type_repetition_in_bounds)] +#![deny(clippy::type_repetition_in_bounds)] + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; pub fn foo(_t: T) where @@ -16,4 +18,21 @@ where unimplemented!(); } +trait LintBounds +where + Self: Clone, + Self: Copy + Default + Ord, + Self: Add + AddAssign + Sub + SubAssign, + Self: Mul + MulAssign + Div + DivAssign, +{ +} + +trait LotsOfBounds +where + Self: Clone + Copy + Default + Ord, + Self: Add + AddAssign + Sub + SubAssign, + Self: Mul + MulAssign + Div + DivAssign, +{ +} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 4264e2e10bf..6a1073a23f6 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,15 +1,23 @@ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:6:5 + --> $DIR/type_repetition_in_bounds.rs:8:5 | LL | T: Clone, | ^^^^^^^^ | note: the lint level is defined here - --> $DIR/type_repetition_in_bounds.rs:1:8 + --> $DIR/type_repetition_in_bounds.rs:1:9 | -LL | #[deny(clippy::type_repetition_in_bounds)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::type_repetition_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider combining the bounds: `T: Copy + Clone` -error: aborting due to previous error +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:24:5 + | +LL | Self: Copy + Default + Ord, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` + +error: aborting due to 2 previous errors From d5a8f03a350e8a392f0aa1c05707b503f549e90b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 3 Jul 2020 10:29:14 +0200 Subject: [PATCH 0237/1110] Take generic args into account for bounded type --- clippy_lints/src/utils/hir_utils.rs | 19 ++++++++++++------- tests/ui/type_repetition_in_bounds.rs | 15 +++++++++++++++ tests/ui/type_repetition_in_bounds.stderr | 2 +- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index ae58f0a1521..34341594c19 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -703,6 +703,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } for segment in path.segments { segment.ident.name.hash(&mut self.s); + self.hash_generic_args(segment.generic_args().args); } }, QPath::TypeRelative(ref ty, ref segment) => { @@ -711,13 +712,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { }, }, TyKind::OpaqueDef(_, arg_list) => { - for arg in *arg_list { - match arg { - GenericArg::Lifetime(ref l) => self.hash_lifetime(l), - GenericArg::Type(ref ty) => self.hash_ty(&ty), - GenericArg::Const(ref ca) => self.hash_body(ca.value.body), - } - } + self.hash_generic_args(arg_list); }, TyKind::TraitObject(_, lifetime) => { self.hash_lifetime(lifetime); @@ -735,4 +730,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(&self.cx.tcx.hir().body(body_id).value); self.maybe_typeck_tables = old_maybe_typeck_tables; } + + fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) { + for arg in arg_list { + match arg { + GenericArg::Lifetime(ref l) => self.hash_lifetime(l), + GenericArg::Type(ref ty) => self.hash_ty(&ty), + GenericArg::Const(ref ca) => self.hash_body(ca.value.body), + } + } + } } diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 60b994548b5..9f1700567d1 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -18,6 +18,7 @@ where unimplemented!(); } +// Threshold test (see #4380) trait LintBounds where Self: Clone, @@ -35,4 +36,18 @@ where { } +// Generic distinction (see #4323) +pub struct Foo
(A); +pub struct Bar { + a: Foo, + b: Foo, +} + +impl Unpin for Bar +where + Foo: Unpin, + Foo: Unpin, +{ +} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 6a1073a23f6..148c19c7d07 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] = help: consider combining the bounds: `T: Copy + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:24:5 + --> $DIR/type_repetition_in_bounds.rs:25:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ From 2d5930a3da7048d784489f28b44a769880b6ceff Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 3 Jul 2020 11:48:28 +0200 Subject: [PATCH 0238/1110] Don't lint for predicates generated in macros --- clippy_lints/src/trait_bounds.rs | 15 +++++++---- tests/ui/type_repetition_in_bounds.rs | 37 ++++++++++++++++++++------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 650edbb4b11..0ef70311fb1 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,5 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{GenericBound, Generics, WherePredicate}; @@ -11,6 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** Repeating the type for every bound makes the code /// less readable than combining the bounds /// + /// **Known problems:** None. + /// /// **Example:** /// ```rust /// pub fn foo(t: T) where T: Copy, T: Clone {} @@ -53,12 +56,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut map = FxHashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { - if let WherePredicate::BoundPredicate(ref p) = bound { - if p.bounds.len() as u64 > self.max_trait_bounds { - return; - } + if_chain! { + if let WherePredicate::BoundPredicate(ref p) = bound; + if p.bounds.len() as u64 <= self.max_trait_bounds; + if !in_macro(p.span); let h = hash(&p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()) { + if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()); + + then { let mut hint_string = format!( "consider combining the bounds: `{}:", snippet(cx, p.bounded_ty.span, "_") diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 9f1700567d1..766190f2099 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -37,17 +37,36 @@ where } // Generic distinction (see #4323) -pub struct Foo(A); -pub struct Bar { - a: Foo, - b: Foo, +mod issue4323 { + pub struct Foo(A); + pub struct Bar { + a: Foo, + b: Foo, + } + + impl Unpin for Bar + where + Foo: Unpin, + Foo: Unpin, + { + } } -impl Unpin for Bar -where - Foo: Unpin, - Foo: Unpin, -{ +// Extern macros shouldn't lint (see #4326) +extern crate serde; +mod issue4326 { + use serde::{Deserialize, Serialize}; + + trait Foo {} + impl Foo for String {} + + #[derive(Debug, Serialize, Deserialize)] + struct Bar + where + S: Foo, + { + foo: S, + } } fn main() {} From c3c402783f1d12852982f9dc734cc4c07b9fce33 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Mon, 4 Nov 2019 19:39:03 +0100 Subject: [PATCH 0239/1110] Added restriction lint: pattern-type-mismatch --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/pattern_type_mismatch.rs | 276 ++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/pattern_type_mismatch/mutability.rs | 40 +++ .../pattern_type_mismatch/mutability.stderr | 19 ++ .../pattern_alternatives.rs | 24 ++ .../pattern_alternatives.stderr | 27 ++ .../pattern_type_mismatch/pattern_structs.rs | 45 +++ .../pattern_structs.stderr | 67 +++++ .../pattern_type_mismatch/pattern_tuples.rs | 57 ++++ .../pattern_tuples.stderr | 83 ++++++ tests/ui/pattern_type_mismatch/syntax.rs | 146 +++++++++ tests/ui/pattern_type_mismatch/syntax.stderr | 78 +++++ 14 files changed, 874 insertions(+) create mode 100644 clippy_lints/src/pattern_type_mismatch.rs create mode 100644 tests/ui/pattern_type_mismatch/mutability.rs create mode 100644 tests/ui/pattern_type_mismatch/mutability.stderr create mode 100644 tests/ui/pattern_type_mismatch/pattern_alternatives.rs create mode 100644 tests/ui/pattern_type_mismatch/pattern_alternatives.stderr create mode 100644 tests/ui/pattern_type_mismatch/pattern_structs.rs create mode 100644 tests/ui/pattern_type_mismatch/pattern_structs.stderr create mode 100644 tests/ui/pattern_type_mismatch/pattern_tuples.rs create mode 100644 tests/ui/pattern_type_mismatch/pattern_tuples.stderr create mode 100644 tests/ui/pattern_type_mismatch/syntax.rs create mode 100644 tests/ui/pattern_type_mismatch/syntax.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b88044d6ce8..ed8f16e65bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1588,6 +1588,7 @@ Released 2018-09-13 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d31a597b66a..4890349999f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -268,6 +268,7 @@ mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; +pub mod pattern_type_mismatch; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -741,6 +742,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &panic_unimplemented::UNREACHABLE, &partialeq_ne_impl::PARTIALEQ_NE_IMPL, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, + &pattern_type_mismatch::PATTERN_TYPE_MISMATCH, &precedence::PRECEDENCE, &ptr::CMP_NULL, &ptr::MUT_FROM_REF, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); + store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1097,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), LintId::of(&panic_unimplemented::UNREACHABLE), + LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH), LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs new file mode 100644 index 00000000000..a07279c92b0 --- /dev/null +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -0,0 +1,276 @@ +use crate::utils::{last_path_segment, span_help_and_lint}; +use rustc::lint::in_external_macro; +use rustc::ty::subst::SubstsRef; +use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; +use rustc_hir::{ + intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, + QPath, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for patterns that aren't exact representations of the types + /// they are applied to. + /// + /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable + /// because it increases ownership hints in the code, and will guard against some changes + /// in ownership. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // Bad + /// let value = &Some(Box::new(23)); + /// match value { + /// Some(inner) => println!("{}", inner), + /// None => println!("none"), + /// } + /// + /// // Good + /// let value = &Some(Box::new(23)); + /// match *value { + /// Some(ref inner) => println!("{}", inner), + /// None => println!("none"), + /// } + /// ``` + pub PATTERN_TYPE_MISMATCH, + restriction, + "type of pattern does not match the expression type" +} + +declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Local(ref local) = stmt.kind { + if let Some(init) = &local.init { + if let Some(init_ty) = cx.tables.node_type_opt(init.hir_id) { + let pat = &local.pat; + if in_external_macro(cx.sess(), pat.span) { + return; + } + let deref_possible = match local.source { + LocalSource::Normal => DerefPossible::Possible, + _ => DerefPossible::Impossible, + }; + apply_lint(cx, pat, init_ty, deref_possible); + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(ref expr, arms, source) = expr.kind { + match source { + MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { + if let Some(expr_ty) = cx.tables.node_type_opt(expr.hir_id) { + 'pattern_checks: for arm in arms { + let pat = &arm.pat; + if in_external_macro(cx.sess(), pat.span) { + continue 'pattern_checks; + } + if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) { + break 'pattern_checks; + } + } + } + }, + _ => (), + } + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: intravisit::FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + hir_id: HirId, + ) { + if let Some(fn_sig) = cx.tables.liberated_fn_sigs().get(hir_id) { + for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { + apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); + } + } + } +} + +#[derive(Debug, Clone, Copy)] +enum DerefPossible { + Possible, + Impossible, +} + +fn apply_lint<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &Pat<'_>, + expr_ty: Ty<'tcx>, + deref_possible: DerefPossible, +) -> bool { + let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); + if let Some((span, mutability, level)) = maybe_mismatch { + span_help_and_lint( + cx, + PATTERN_TYPE_MISMATCH, + span, + "type of pattern does not match the expression type", + &format!( + "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", + match (deref_possible, level) { + (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", + _ => "", + }, + match mutability { + Mutability::Mut => "&mut _", + Mutability::Not => "&_", + }, + ), + ); + true + } else { + false + } +} + +#[derive(Debug, Copy, Clone)] +enum Level { + Top, + Lower, +} + +#[allow(rustc::usage_of_ty_tykind)] +fn find_first_mismatch<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &Pat<'_>, + ty: Ty<'tcx>, + level: Level, +) -> Option<(Span, Mutability, Level)> { + if let PatKind::Ref(ref sub_pat, _) = pat.kind { + if let TyKind::Ref(_, sub_ty, _) = ty.kind { + return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); + } + } + + if let TyKind::Ref(_, _, mutability) = ty.kind { + if is_non_ref_pattern(&pat.kind) { + return Some((pat.span, mutability, level)); + } + } + + if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let Some(variant) = get_variant(adt_def, qpath) { + let field_defs = &variant.fields; + return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); + } + } + } + + if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let Some(variant) = get_variant(adt_def, qpath) { + let field_defs = &variant.fields; + let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); + return find_first_mismatch_in_tuple(cx, pats, ty_iter); + } + } + } + + if let PatKind::Tuple(ref pats, _) = pat.kind { + if let TyKind::Tuple(..) = ty.kind { + return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); + } + } + + if let PatKind::Or(sub_pats) = pat.kind { + for pat in sub_pats { + let maybe_mismatch = find_first_mismatch(cx, pat, ty, level); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + } + } + + None +} + +fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> { + if adt_def.is_struct() { + if let Some(variant) = adt_def.variants.iter().next() { + return Some(variant); + } + } + + if adt_def.is_enum() { + let pat_ident = last_path_segment(qpath).ident; + for variant in &adt_def.variants { + if variant.ident == pat_ident { + return Some(variant); + } + } + } + + None +} + +fn find_first_mismatch_in_tuple<'a, 'tcx, I>( + cx: &LateContext<'a, 'tcx>, + pats: &[&Pat<'_>], + ty_iter_src: I, +) -> Option<(Span, Mutability, Level)> +where + I: IntoIterator>, +{ + let mut field_tys = ty_iter_src.into_iter(); + 'fields: for pat in pats { + let field_ty = if let Some(ty) = field_tys.next() { + ty + } else { + break 'fields; + }; + + let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + } + + None +} + +fn find_first_mismatch_in_struct<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + field_pats: &[FieldPat<'_>], + field_defs: &[FieldDef], + substs_ref: SubstsRef<'tcx>, +) -> Option<(Span, Mutability, Level)> { + for field_pat in field_pats { + 'definitions: for field_def in field_defs { + if field_pat.ident == field_def.ident { + let field_ty = field_def.ty(cx.tcx, substs_ref); + let pat = &field_pat.pat; + let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + break 'definitions; + } + } + } + + None +} + +fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool { + match pat_kind { + PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true, + PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)), + _ => false, + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a2998d74130..6402efc2521 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1697,6 +1697,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "path_buf_push_overwrite", }, + Lint { + name: "pattern_type_mismatch", + group: "restriction", + desc: "type of pattern does not match the expression type", + deprecation: None, + module: "pattern_type_mismatch", + }, Lint { name: "possible_missing_comma", group: "correctness", diff --git a/tests/ui/pattern_type_mismatch/mutability.rs b/tests/ui/pattern_type_mismatch/mutability.rs new file mode 100644 index 00000000000..9b4f2f1f579 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/mutability.rs @@ -0,0 +1,40 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn should_lint() { + let value = &Some(23); + match value { + Some(_) => (), + _ => (), + } + + let value = &mut Some(23); + match value { + Some(_) => (), + _ => (), + } +} + +fn should_not_lint() { + let value = &Some(23); + match value { + &Some(_) => (), + _ => (), + } + match *value { + Some(_) => (), + _ => (), + } + + let value = &mut Some(23); + match value { + &mut Some(_) => (), + _ => (), + } + match *value { + Some(_) => (), + _ => (), + } +} diff --git a/tests/ui/pattern_type_mismatch/mutability.stderr b/tests/ui/pattern_type_mismatch/mutability.stderr new file mode 100644 index 00000000000..3421d568365 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/mutability.stderr @@ -0,0 +1,19 @@ +error: type of pattern does not match the expression type + --> $DIR/mutability.rs:9:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/mutability.rs:15:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs new file mode 100644 index 00000000000..065ea9fb9b5 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs @@ -0,0 +1,24 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn alternatives() { + enum Value<'a> { + Unused, + A(&'a Option), + B, + } + let ref_value = &Value::A(&Some(23)); + + // not ok + if let Value::B | Value::A(_) = ref_value {} + if let &Value::B | &Value::A(Some(_)) = ref_value {} + if let Value::B | Value::A(Some(_)) = *ref_value {} + + // ok + if let &Value::B | &Value::A(_) = ref_value {} + if let Value::B | Value::A(_) = *ref_value {} + if let &Value::B | &Value::A(&Some(_)) = ref_value {} + if let Value::B | Value::A(&Some(_)) = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr new file mode 100644 index 00000000000..d285c93782c --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr @@ -0,0 +1,27 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:15:12 + | +LL | if let Value::B | Value::A(_) = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:16:34 + | +LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:17:32 + | +LL | if let Value::B | Value::A(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 3 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.rs b/tests/ui/pattern_type_mismatch/pattern_structs.rs new file mode 100644 index 00000000000..417b1c107c5 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_structs.rs @@ -0,0 +1,45 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn struct_types() { + struct Struct<'a> { + ref_inner: &'a Option, + } + let ref_value = &Struct { ref_inner: &Some(42) }; + + // not ok + let Struct { .. } = ref_value; + if let &Struct { ref_inner: Some(_) } = ref_value {} + if let Struct { ref_inner: Some(_) } = *ref_value {} + + // ok + let &Struct { .. } = ref_value; + let Struct { .. } = *ref_value; + if let &Struct { ref_inner: &Some(_) } = ref_value {} + if let Struct { ref_inner: &Some(_) } = *ref_value {} +} + +fn struct_enum_variants() { + enum StructEnum<'a> { + Empty, + Var { inner_ref: &'a Option }, + } + let ref_value = &StructEnum::Var { inner_ref: &Some(42) }; + + // not ok + if let StructEnum::Var { .. } = ref_value {} + if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} + if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} + if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} + if let StructEnum::Empty = ref_value {} + + // ok + if let &StructEnum::Var { .. } = ref_value {} + if let StructEnum::Var { .. } = *ref_value {} + if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {} + if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {} + if let &StructEnum::Empty = ref_value {} + if let StructEnum::Empty = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/tests/ui/pattern_type_mismatch/pattern_structs.stderr new file mode 100644 index 00000000000..d428e85b0c9 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_structs.stderr @@ -0,0 +1,67 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:13:9 + | +LL | let Struct { .. } = ref_value; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:14:33 + | +LL | if let &Struct { ref_inner: Some(_) } = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:15:32 + | +LL | if let Struct { ref_inner: Some(_) } = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:32:12 + | +LL | if let StructEnum::Var { .. } = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:33:12 + | +LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:34:42 + | +LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:35:41 + | +LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:36:12 + | +LL | if let StructEnum::Empty = ref_value {} + | ^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 8 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/tests/ui/pattern_type_mismatch/pattern_tuples.rs new file mode 100644 index 00000000000..19504a051d8 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_tuples.rs @@ -0,0 +1,57 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn tuple_types() { + struct TupleStruct<'a>(&'a Option); + let ref_value = &TupleStruct(&Some(42)); + + // not ok + let TupleStruct(_) = ref_value; + if let &TupleStruct(Some(_)) = ref_value {} + if let TupleStruct(Some(_)) = *ref_value {} + + // ok + let &TupleStruct(_) = ref_value; + let TupleStruct(_) = *ref_value; + if let &TupleStruct(&Some(_)) = ref_value {} + if let TupleStruct(&Some(_)) = *ref_value {} +} + +fn tuple_enum_variants() { + enum TupleEnum<'a> { + Empty, + Var(&'a Option), + } + let ref_value = &TupleEnum::Var(&Some(42)); + + // not ok + if let TupleEnum::Var(_) = ref_value {} + if let &TupleEnum::Var(Some(_)) = ref_value {} + if let TupleEnum::Var(Some(_)) = *ref_value {} + if let TupleEnum::Empty = ref_value {} + + // ok + if let &TupleEnum::Var(_) = ref_value {} + if let TupleEnum::Var(_) = *ref_value {} + if let &TupleEnum::Var(&Some(_)) = ref_value {} + if let TupleEnum::Var(&Some(_)) = *ref_value {} + if let &TupleEnum::Empty = ref_value {} + if let TupleEnum::Empty = *ref_value {} +} + +fn plain_tuples() { + let ref_value = &(&Some(23), &Some(42)); + + // not ok + let (_a, _b) = ref_value; + if let &(_a, Some(_)) = ref_value {} + if let (_a, Some(_)) = *ref_value {} + + // ok + let &(_a, _b) = ref_value; + let (_a, _b) = *ref_value; + if let &(_a, &Some(_)) = ref_value {} + if let (_a, &Some(_)) = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr new file mode 100644 index 00000000000..edd0074d00d --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr @@ -0,0 +1,83 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:11:9 + | +LL | let TupleStruct(_) = ref_value; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:12:25 + | +LL | if let &TupleStruct(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:13:24 + | +LL | if let TupleStruct(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:30:12 + | +LL | if let TupleEnum::Var(_) = ref_value {} + | ^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:31:28 + | +LL | if let &TupleEnum::Var(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:32:27 + | +LL | if let TupleEnum::Var(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:33:12 + | +LL | if let TupleEnum::Empty = ref_value {} + | ^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:48:9 + | +LL | let (_a, _b) = ref_value; + | ^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:49:18 + | +LL | if let &(_a, Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:50:17 + | +LL | if let (_a, Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 10 previous errors + diff --git a/tests/ui/pattern_type_mismatch/syntax.rs b/tests/ui/pattern_type_mismatch/syntax.rs new file mode 100644 index 00000000000..e89917c41e8 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/syntax.rs @@ -0,0 +1,146 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn syntax_match() { + let ref_value = &Some(&Some(42)); + + // not ok + match ref_value { + Some(_) => (), + None => (), + } + + // ok + match ref_value { + &Some(_) => (), + &None => (), + } + match *ref_value { + Some(_) => (), + None => (), + } +} + +fn syntax_if_let() { + let ref_value = &Some(42); + + // not ok + if let Some(_) = ref_value {} + + // ok + if let &Some(_) = ref_value {} + if let Some(_) = *ref_value {} +} + +fn syntax_while_let() { + let ref_value = &Some(42); + + // not ok + while let Some(_) = ref_value { + break; + } + + // ok + while let &Some(_) = ref_value { + break; + } + while let Some(_) = *ref_value { + break; + } +} + +fn syntax_for() { + let ref_value = &Some(23); + let slice = &[(2, 3), (4, 2)]; + + // not ok + for (_a, _b) in slice.iter() {} + + // ok + for &(_a, _b) in slice.iter() {} +} + +fn syntax_let() { + let ref_value = &(2, 3); + + // not ok + let (_n, _m) = ref_value; + + // ok + let &(_n, _m) = ref_value; + let (_n, _m) = *ref_value; +} + +fn syntax_fn() { + // not ok + fn foo((_a, _b): &(i32, i32)) {} + + // ok + fn foo_ok_1(&(_a, _b): &(i32, i32)) {} +} + +fn syntax_closure() { + fn foo(f: F) + where + F: FnOnce(&(i32, i32)), + { + } + + // not ok + foo(|(_a, _b)| ()); + + // ok + foo(|&(_a, _b)| ()); +} + +fn macro_with_expression() { + macro_rules! matching_macro { + ($e:expr) => { + $e + }; + } + let value = &Some(23); + + // not ok + matching_macro!(match value { + Some(_) => (), + _ => (), + }); + + // ok + matching_macro!(match value { + &Some(_) => (), + _ => (), + }); + matching_macro!(match *value { + Some(_) => (), + _ => (), + }); +} + +fn macro_expansion() { + macro_rules! matching_macro { + ($e:expr) => { + // not ok + match $e { + Some(_) => (), + _ => (), + } + + // ok + match $e { + &Some(_) => (), + _ => (), + } + match *$e { + Some(_) => (), + _ => (), + } + }; + } + + let value = &Some(23); + matching_macro!(value); +} diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr new file mode 100644 index 00000000000..110e8421a5e --- /dev/null +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -0,0 +1,78 @@ +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:11:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:30:12 + | +LL | if let Some(_) = ref_value {} + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:41:15 + | +LL | while let Some(_) = ref_value { + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:59:9 + | +LL | for (_a, _b) in slice.iter() {} + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:69:9 + | +LL | let (_n, _m) = ref_value; + | ^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:78:12 + | +LL | fn foo((_a, _b): &(i32, i32)) {} + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:92:10 + | +LL | foo(|(_a, _b)| ()); + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:108:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:128:17 + | +LL | Some(_) => (), + | ^^^^^^^ +... +LL | matching_macro!(value); + | ----------------------- in this macro invocation + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 9 previous errors + From 55877d7b4a6a2c713412db7b30ef79b2a252956e Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Sun, 9 Feb 2020 17:57:35 +0100 Subject: [PATCH 0240/1110] span_help_and_lint -> span_lint_and_help --- clippy_lints/src/pattern_type_mismatch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a07279c92b0..d3f65db1916 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,4 +1,4 @@ -use crate::utils::{last_path_segment, span_help_and_lint}; +use crate::utils::{last_path_segment, span_lint_and_help}; use rustc::lint::in_external_macro; use rustc::ty::subst::SubstsRef; use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; @@ -115,7 +115,7 @@ fn apply_lint<'a, 'tcx>( ) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); if let Some((span, mutability, level)) = maybe_mismatch { - span_help_and_lint( + span_lint_and_help( cx, PATTERN_TYPE_MISMATCH, span, From 346ee968bbe8925aa7961a3e2c99e7ef84c74623 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Mon, 10 Feb 2020 22:19:19 +0100 Subject: [PATCH 0241/1110] Adjusted expected STDERR --- tests/ui/pattern_type_mismatch/syntax.stderr | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr index 110e8421a5e..5a5186bd4fc 100644 --- a/tests/ui/pattern_type_mismatch/syntax.stderr +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -73,6 +73,7 @@ LL | matching_macro!(value); | ----------------------- in this macro invocation | = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 9 previous errors From 6447507ab150ebd1787ca6af385db49dfdf45978 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 8 Jun 2020 15:10:57 +0200 Subject: [PATCH 0242/1110] Fix rebase fallout --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4890349999f..3d8ce10aef3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -268,7 +268,7 @@ mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; -pub mod pattern_type_mismatch; +mod pattern_type_mismatch; mod precedence; mod ptr; mod ptr_offset_with_cast; diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index d3f65db1916..a6079a8b5bc 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,12 +1,12 @@ use crate::utils::{last_path_segment, span_lint_and_help}; -use rustc::lint::in_external_macro; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; use rustc_hir::{ intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -120,6 +120,7 @@ fn apply_lint<'a, 'tcx>( PATTERN_TYPE_MISMATCH, span, "type of pattern does not match the expression type", + None, &format!( "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", match (deref_possible, level) { From 92ecc53691f3edd67526dca423a2b4083d12a221 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Wed, 1 Jul 2020 15:49:06 +0200 Subject: [PATCH 0243/1110] Catching up with rustc changes --- clippy_lints/src/pattern_type_mismatch.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a6079a8b5bc..fcc9b16068f 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Local(ref local) = stmt.kind { if let Some(init) = &local.init { - if let Some(init_ty) = cx.tables.node_type_opt(init.hir_id) { + if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) { let pat = &local.pat; if in_external_macro(cx.sess(), pat.span) { return; @@ -67,7 +67,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { if let ExprKind::Match(ref expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { - if let Some(expr_ty) = cx.tables.node_type_opt(expr.hir_id) { + if let Some(expr_ty) = cx.tables().node_type_opt(expr.hir_id) { 'pattern_checks: for arm in arms { let pat = &arm.pat; if in_external_macro(cx.sess(), pat.span) { @@ -93,7 +93,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { _: Span, hir_id: HirId, ) { - if let Some(fn_sig) = cx.tables.liberated_fn_sigs().get(hir_id) { + if let Some(fn_sig) = cx.tables().liberated_fn_sigs().get(hir_id) { for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); } From d617551a6a3830a5324898f2046b97aad8c6067a Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Wed, 1 Jul 2020 15:49:46 +0200 Subject: [PATCH 0244/1110] Expanded lint documentation --- clippy_lints/src/pattern_type_mismatch.rs | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index fcc9b16068f..9fa5860ba30 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -14,6 +14,22 @@ declare_clippy_lint! { /// **What it does:** Checks for patterns that aren't exact representations of the types /// they are applied to. /// + /// To satisfy this lint, you will have to adjust either the expression that is matched + /// against or the pattern itself, as well as the bindings that are introduced by the + /// adjusted patterns. For matching you will have to either dereference the expression + /// with the `*` operator, or amend the patterns to explicitly match against `&` + /// or `&mut ` depending on the reference mutability. For the bindings you need + /// to use the inverse. You can leave them as plain bindings if you wish for the value + /// to be copied, but you must use `ref mut ` or `ref ` to construct + /// a reference into the matched structure. + /// + /// If you are looking for a way to learn about ownership semantics in more detail, it + /// is recommended to look at IDE options available to you to highlight types, lifetimes + /// and reference semantics in your code. The available tooling would expose these things + /// in a general way even outside of the various pattern matching mechanics. Of course + /// this lint can still be used to highlight areas of interest and ensure a good understanding + /// of ownership semantics. + /// /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable /// because it increases ownership hints in the code, and will guard against some changes /// in ownership. @@ -22,6 +38,10 @@ declare_clippy_lint! { /// /// **Example:** /// + /// This example shows the basic adjustments necessary to satisfy the lint. Note how + /// the matched expression is explicitly dereferenced with `*` and the `inner` variable + /// is bound to a shared borrow via `ref inner`. + /// /// ```rust,ignore /// // Bad /// let value = &Some(Box::new(23)); @@ -37,6 +57,25 @@ declare_clippy_lint! { /// None => println!("none"), /// } /// ``` + /// + /// The following example demonstrates one of the advantages of the more verbose style. + /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable + /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot + /// accidentally modify the wrong part of the structure. + /// + /// ```rust,ignore + /// // Bad + /// let mut values = vec![(2, 3), (3, 4)]; + /// for (a, b) in &mut values { + /// *a += *b; + /// } + /// + /// // Good + /// let mut values = vec![(2, 3), (3, 4)]; + /// for &mut (ref mut a, b) in &mut values { + /// *a += b; + /// } + /// ``` pub PATTERN_TYPE_MISMATCH, restriction, "type of pattern does not match the expression type" From aa4bee228f23b3e1a1d91ed3a4606af3c6b60895 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Fri, 3 Jul 2020 18:20:19 +0200 Subject: [PATCH 0245/1110] LateContext has only one lifetime parameter now --- clippy_lints/src/pattern_type_mismatch.rs | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 9fa5860ba30..8587a79e821 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -83,8 +83,8 @@ declare_clippy_lint! { declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { - fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { +impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Local(ref local) = stmt.kind { if let Some(init) = &local.init { if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) { @@ -102,7 +102,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { } } - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(ref expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { @@ -125,7 +125,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { fn check_fn( &mut self, - cx: &LateContext<'a, 'tcx>, + cx: &LateContext<'tcx>, _: intravisit::FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, @@ -146,8 +146,8 @@ enum DerefPossible { Impossible, } -fn apply_lint<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn apply_lint<'tcx>( + cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible, @@ -185,8 +185,8 @@ enum Level { } #[allow(rustc::usage_of_ty_tykind)] -fn find_first_mismatch<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn find_first_mismatch<'tcx>( + cx: &LateContext<'tcx>, pat: &Pat<'_>, ty: Ty<'tcx>, level: Level, @@ -259,8 +259,8 @@ fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a Variant None } -fn find_first_mismatch_in_tuple<'a, 'tcx, I>( - cx: &LateContext<'a, 'tcx>, +fn find_first_mismatch_in_tuple<'tcx, I>( + cx: &LateContext<'tcx>, pats: &[&Pat<'_>], ty_iter_src: I, ) -> Option<(Span, Mutability, Level)> @@ -284,8 +284,8 @@ where None } -fn find_first_mismatch_in_struct<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn find_first_mismatch_in_struct<'tcx>( + cx: &LateContext<'tcx>, field_pats: &[FieldPat<'_>], field_defs: &[FieldDef], substs_ref: SubstsRef<'tcx>, From c0fd452840c7cc53b3396268f62ed2a0a2e8fef7 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Fri, 3 Jul 2020 18:23:36 +0200 Subject: [PATCH 0246/1110] fmt fix --- clippy_lints/src/pattern_type_mismatch.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 8587a79e821..a49dc87c0b4 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -146,12 +146,7 @@ enum DerefPossible { Impossible, } -fn apply_lint<'tcx>( - cx: &LateContext<'tcx>, - pat: &Pat<'_>, - expr_ty: Ty<'tcx>, - deref_possible: DerefPossible, -) -> bool { +fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); if let Some((span, mutability, level)) = maybe_mismatch { span_lint_and_help( From bf48a2d50d82cccac58d7c4c73700eaf66926aee Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 5 Mar 2020 10:35:05 -0800 Subject: [PATCH 0247/1110] Lint for if let Some(x) = ... instead of Option::map_or --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/option_if_let_else.rs | 202 +++++++++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/option_if_let_else.fixed | 48 ++++++ tests/ui/option_if_let_else.rs | 56 +++++++ tests/ui/option_if_let_else.stderr | 62 ++++++++ 7 files changed, 381 insertions(+) create mode 100644 clippy_lints/src/option_if_let_else.rs create mode 100644 tests/ui/option_if_let_else.fixed create mode 100644 tests/ui/option_if_let_else.rs create mode 100644 tests/ui/option_if_let_else.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ed8f16e65bb..1a081bb85fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1577,6 +1577,7 @@ Released 2018-09-13 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d5be893ffb..cd91e7ceb32 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -264,6 +264,7 @@ mod non_copy_const; mod non_expressive_names; mod open_options; mod option_env_unwrap; +mod option_if_let_else; mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; @@ -734,6 +735,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &non_expressive_names::SIMILAR_NAMES, &open_options::NONSENSICAL_OPEN_OPTIONS, &option_env_unwrap::OPTION_ENV_UNWRAP, + &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, @@ -1052,6 +1054,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); @@ -1369,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), @@ -1517,6 +1521,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs new file mode 100644 index 00000000000..f092f1297c1 --- /dev/null +++ b/clippy_lints/src/option_if_let_else.rs @@ -0,0 +1,202 @@ +use crate::utils::sugg::Sugg; +use crate::utils::{match_type, paths, span_lint_and_sugg}; +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::marker::PhantomData; + +declare_clippy_lint! { + /// **What it does:** + /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// idiomatically done with `Option::map_or` (if the else bit is a simple + /// expression) or `Option::map_or_else` (if the else bit is a longer + /// block). + /// + /// **Why is this bad?** + /// Using the dedicated functions of the Option type is clearer and + /// more concise than an if let expression. + /// + /// **Known problems:** + /// This lint uses whether the block is just an expression or if it has + /// more statements to decide whether to use `Option::map_or` or + /// `Option::map_or_else`. If you have a single expression which calls + /// an expensive function, then it would be more efficient to use + /// `Option::map_or_else`, but this lint would suggest `Option::map_or`. + /// + /// Also, this lint uses a deliberately conservative metric for checking + /// if the inside of either body contains breaks or continues which will + /// cause it to not suggest a fix if either block contains a loop with + /// continues or breaks contained within the loop. + /// + /// **Example:** + /// + /// ```rust + /// # let optional: Option = Some(0); + /// let _ = if let Some(foo) = optional { + /// foo + /// } else { + /// 5 + /// }; + /// let _ = if let Some(foo) = optional { + /// foo + /// } else { + /// let y = do_complicated_function(); + /// y*y + /// }; + /// ``` + /// + /// should be + /// + /// ```rust + /// # let optional: Option = Some(0); + /// let _ = optional.map_or(5, |foo| foo); + /// let _ = optional.map_or_else(||{ + /// let y = do_complicated_function; + /// y*y + /// }, |foo| foo); + /// ``` + pub OPTION_IF_LET_ELSE, + style, + "reimplementation of Option::map_or" +} + +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); + +/// Returns true iff the given expression is the result of calling Result::ok +fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind; + if path.ident.name.to_ident_string() == "ok"; + if match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT); + then { + true + } else { + false + } + } +} + +/// A struct containing information about occurences of the +/// `if let Some(..) = .. else` construct that this lint detects. +struct OptionIfLetElseOccurence { + option: String, + method_sugg: String, + some_expr: String, + none_expr: String, +} + +struct ReturnBreakContinueVisitor<'tcx> { + seen_return_break_continue: bool, + phantom_data: PhantomData<&'tcx bool>, +} +impl<'tcx> ReturnBreakContinueVisitor<'tcx> { + fn new() -> ReturnBreakContinueVisitor<'tcx> { + ReturnBreakContinueVisitor { + seen_return_break_continue: false, + phantom_data: PhantomData, + } + } +} +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + rustc_hir::intravisit::walk_expr(self, ex); + }, + } + } +} + +fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { + let mut recursive_visitor: ReturnBreakContinueVisitor<'tcx> = ReturnBreakContinueVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} + +/// If this expression is the option if let/else construct we're detecting, then +/// this function returns an OptionIfLetElseOccurence struct with details if +/// this construct is found, or None if this construct is not found. +fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { + //(String, String, String, String)> { + if_chain! { + if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if arms.len() == 2; + if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); + if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already + if let PatKind::TupleStruct(_, &[inner_pat], _) = &arms[0].pat.kind; + if let PatKind::Binding(_, _, id, _) = &inner_pat.kind; + if !contains_return_break_continue(arms[0].body); + if !contains_return_break_continue(arms[1].body); + then { + let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) + = &arms[0].body.kind { + if let &[] = &statements { + expr + } else { + &arms[0].body + } + } else { + return None; + }; + let (none_body, method_sugg) = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) + = &arms[1].body.kind { + if let &[] = &statements { + (expr, "map_or") + } else { + (&arms[1].body, "map_or_else") + } + } else { + return None; + }; + let capture_name = id.name.to_ident_string(); + Some(OptionIfLetElseOccurence { + option: format!("{}", Sugg::hir(cx, let_body, "..")), + method_sugg: format!("{}", method_sugg), + some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), + none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")) + }) + } else { + None + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let Some(detection) = detect_option_if_let_else(cx, expr) { + span_lint_and_sugg( + cx, + OPTION_IF_LET_ELSE, + expr.span, + format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), + "try", + format!( + "{}.{}({}, {})", + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6402efc2521..b499d565fa7 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1620,6 +1620,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "option_env_unwrap", }, + Lint { + name: "option_if_let_else", + group: "style", + desc: "reimplementation of Option::map_or", + deprecation: None, + module: "option_if_let_else", + }, Lint { name: "option_map_or_none", group: "style", diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed new file mode 100644 index 00000000000..3aa895120d1 --- /dev/null +++ b/tests/ui/option_if_let_else.fixed @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::option_if_let_else)] + +fn bad1(string: Option<&str>) -> (bool, &str) { + string.map_or((false, "hello"), |x| (true, x)) +} + +fn longer_body(arg: Option) -> u32 { + arg.map_or(13, |x| { + let y = x * x; + y * y + }) +} + +fn test_map_or_else(arg: Option) { + let _ = arg.map_or_else(|| { + let mut y = 1; + y = (y + 2 / y) / 2; + y = (y + 2 / y) / 2; + y + }, |x| x * x * x * x); +} + +fn negative_tests(arg: Option) -> u32 { + let _ = if let Some(13) = arg { "unlucky" } else { "lucky" }; + for _ in 0..10 { + let _ = if let Some(x) = arg { + x + } else { + continue; + }; + } + let _ = if let Some(x) = arg { + return x; + } else { + 5 + }; + 7 +} + +fn main() { + let optional = Some(5); + let _ = optional.map_or(5, |x| x + 2); + let _ = bad1(None); + let _ = longer_body(None); + test_map_or_else(None); + let _ = negative_tests(None); +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs new file mode 100644 index 00000000000..7d029b0bcf4 --- /dev/null +++ b/tests/ui/option_if_let_else.rs @@ -0,0 +1,56 @@ +// run-rustfix +#![warn(clippy::option_if_let_else)] + +fn bad1(string: Option<&str>) -> (bool, &str) { + if let Some(x) = string { + (true, x) + } else { + (false, "hello") + } +} + +fn longer_body(arg: Option) -> u32 { + if let Some(x) = arg { + let y = x * x; + y * y + } else { + 13 + } +} + +fn test_map_or_else(arg: Option) { + let _ = if let Some(x) = arg { + x * x * x * x + } else { + let mut y = 1; + y = (y + 2 / y) / 2; + y = (y + 2 / y) / 2; + y + }; +} + +fn negative_tests(arg: Option) -> u32 { + let _ = if let Some(13) = arg { "unlucky" } else { "lucky" }; + for _ in 0..10 { + let _ = if let Some(x) = arg { + x + } else { + continue; + }; + } + let _ = if let Some(x) = arg { + return x; + } else { + 5 + }; + 7 +} + +fn main() { + let optional = Some(5); + let _ = if let Some(x) = optional { x + 2 } else { 5 }; + let _ = bad1(None); + let _ = longer_body(None); + test_map_or_else(None); + let _ = negative_tests(None); +} diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr new file mode 100644 index 00000000000..d6cf0836733 --- /dev/null +++ b/tests/ui/option_if_let_else.stderr @@ -0,0 +1,62 @@ +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:5:5 + | +LL | / if let Some(x) = string { +LL | | (true, x) +LL | | } else { +LL | | (false, "hello") +LL | | } + | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))` + | + = note: `-D clippy::option-if-let-else` implied by `-D warnings` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:13:5 + | +LL | / if let Some(x) = arg { +LL | | let y = x * x; +LL | | y * y +LL | | } else { +LL | | 13 +LL | | } + | |_____^ + | +help: try + | +LL | arg.map_or(13, |x| { +LL | let y = x * x; +LL | y * y +LL | }) + | + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:22:13 + | +LL | let _ = if let Some(x) = arg { + | _____________^ +LL | | x * x * x * x +LL | | } else { +LL | | let mut y = 1; +... | +LL | | y +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = arg.map_or_else(|| { +LL | let mut y = 1; +LL | y = (y + 2 / y) / 2; +LL | y = (y + 2 / y) / 2; +LL | y +LL | }, |x| x * x * x * x); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:51:13 + | +LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` + +error: aborting due to 4 previous errors + From 82f8d4d6f1645dd08b107c3ead9155412637739b Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 25 Apr 2020 08:32:33 -0700 Subject: [PATCH 0248/1110] Stop linting on macros and correctly use braces for constructs --- clippy_lints/src/option_if_let_else.rs | 23 ++++++++++++++++++++--- tests/ui/option_if_let_else.fixed | 7 +++++++ tests/ui/option_if_let_else.rs | 11 +++++++++++ tests/ui/option_if_let_else.stderr | 19 +++++++++++++++---- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index f092f1297c1..1edec1cad6e 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,3 +1,4 @@ +use crate::utils; use crate::utils::sugg::Sugg; use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; @@ -89,6 +90,7 @@ struct OptionIfLetElseOccurence { method_sugg: String, some_expr: String, none_expr: String, + wrap_braces: bool, } struct ReturnBreakContinueVisitor<'tcx> { @@ -140,6 +142,7 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { //(String, String, String, String)> { if_chain! { + // if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); @@ -170,11 +173,23 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - return None; }; let capture_name = id.name.to_ident_string(); + let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + if_chain! { + if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr; + if expr.hir_id == arms[1].body.hir_id; + then { + true + } else { + false + } + } + }); Some(OptionIfLetElseOccurence { option: format!("{}", Sugg::hir(cx, let_body, "..")), method_sugg: format!("{}", method_sugg), some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), - none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")) + none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), + wrap_braces, }) } else { None @@ -192,8 +207,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), "try", format!( - "{}.{}({}, {})", - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr + "{}{}.{}({}, {}){}", + if detection.wrap_braces { "{ " } else { "" }, + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + if detection.wrap_braces { " }" } else { "" }, ), Applicability::MachineApplicable, ); diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 3aa895120d1..343e099b2b7 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,6 +5,12 @@ fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) } +fn bad2(string: Option<&str>) -> Option<(bool, &str)> { + if string.is_none() { + None + } else { string.map_or(Some((false, "")), |x| Some((true, x))) } +} + fn longer_body(arg: Option) -> u32 { arg.map_or(13, |x| { let y = x * x; @@ -42,6 +48,7 @@ fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); + let _ = bad2(None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7d029b0bcf4..b0c203f0637 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -9,6 +9,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) { } } +fn bad2(string: Option<&str>) -> Option<(bool, &str)> { + if string.is_none() { + None + } else if let Some(x) = string { + Some((true, x)) + } else { + Some((false, "")) + } +} + fn longer_body(arg: Option) -> u32 { if let Some(x) = arg { let y = x * x; @@ -50,6 +60,7 @@ fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); + let _ = bad2(None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index d6cf0836733..656cfb2f62a 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -11,7 +11,18 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:13:5 + --> $DIR/option_if_let_else.rs:15:12 + | +LL | } else if let Some(x) = string { + | ____________^ +LL | | Some((true, x)) +LL | | } else { +LL | | Some((false, "")) +LL | | } + | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:23:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -30,7 +41,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:22:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -53,10 +64,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:51:13 + --> $DIR/option_if_let_else.rs:61:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From b85796fe3613e20a4af21933783a3d993bb8d7ad Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 25 Apr 2020 09:08:23 -0700 Subject: [PATCH 0249/1110] Properly parenthesize to avoid operator precedence errors --- clippy_lints/src/option_if_let_else.rs | 25 ++++++++++++++++++++++--- tests/ui/option_if_let_else.fixed | 9 +++++++-- tests/ui/option_if_let_else.rs | 13 +++++++++++-- tests/ui/option_if_let_else.stderr | 16 +++++++++++++--- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 1edec1cad6e..66971ee0262 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -175,7 +175,12 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - let capture_name = id.name.to_ident_string(); let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if_chain! { - if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr; + if let Some(Expr { kind: ExprKind::Match( + _, + arms, + MatchSource::IfDesugar{contains_else_clause: true} + | MatchSource::IfLetDesugar{contains_else_clause: true}), + .. } ) = parent.expr; if expr.hir_id == arms[1].body.hir_id; then { true @@ -184,8 +189,19 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } }); + let parens_around_option = match &let_body.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Block(..) + | ExprKind::Field(..) + | ExprKind::Path(_) + => false, + _ => true, + }; Some(OptionIfLetElseOccurence { - option: format!("{}", Sugg::hir(cx, let_body, "..")), + option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }), method_sugg: format!("{}", method_sugg), some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), @@ -209,7 +225,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { format!( "{}{}.{}({}, {}){}", if detection.wrap_braces { "{ " } else { "" }, - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + detection.option, + detection.method_sugg, + detection.none_expr, + detection.some_expr, if detection.wrap_braces { " }" } else { "" }, ), Applicability::MachineApplicable, diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 343e099b2b7..80b162714ac 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,12 +5,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) } -fn bad2(string: Option<&str>) -> Option<(bool, &str)> { +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None } else { string.map_or(Some((false, "")), |x| Some((true, x))) } } +fn unop_bad(string: &Option<&str>) -> usize { + (*string).map_or(0, |s| s.len()) +} + fn longer_body(arg: Option) -> u32 { arg.map_or(13, |x| { let y = x * x; @@ -48,7 +52,8 @@ fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); - let _ = bad2(None); + let _ = else_if_option(None); + let _ = unop_bad(&None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index b0c203f0637..7c43fbea373 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -9,7 +9,7 @@ fn bad1(string: Option<&str>) -> (bool, &str) { } } -fn bad2(string: Option<&str>) -> Option<(bool, &str)> { +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None } else if let Some(x) = string { @@ -19,6 +19,14 @@ fn bad2(string: Option<&str>) -> Option<(bool, &str)> { } } +fn unop_bad(string: &Option<&str>) -> usize { + if let Some(s) = *string { + s.len() + } else { + 0 + } +} + fn longer_body(arg: Option) -> u32 { if let Some(x) = arg { let y = x * x; @@ -60,7 +68,8 @@ fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); - let _ = bad2(None); + let _ = else_if_option(None); + let _ = unop_bad(&None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 656cfb2f62a..b932fe59759 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -24,6 +24,16 @@ LL | | } error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:23:5 | +LL | / if let Some(s) = *string { +LL | | s.len() +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: try: `(*string).map_or(0, |s| s.len())` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:31:5 + | LL | / if let Some(x) = arg { LL | | let y = x * x; LL | | y * y @@ -41,7 +51,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -64,10 +74,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:69:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors From 88c8afdddff07adeff4c87431cbe8bc630a36d68 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 9 May 2020 20:20:57 -0700 Subject: [PATCH 0250/1110] Handle ref, mut, &, and &mut on the option --- clippy_lints/src/option_if_let_else.rs | 28 +++++--- tests/ui/option_if_let_else.fixed | 20 +++++- tests/ui/option_if_let_else.rs | 36 ++++++++-- tests/ui/option_if_let_else.stderr | 99 +++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 66971ee0262..4e501f4ca02 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -140,18 +140,24 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { /// this function returns an OptionIfLetElseOccurence struct with details if /// this construct is found, or None if this construct is not found. fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { - //(String, String, String, String)> { if_chain! { - // if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly + if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; - if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); + // if type_is_option(cx, &cx.tables.expr_ty(let_body).kind); if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already - if let PatKind::TupleStruct(_, &[inner_pat], _) = &arms[0].pat.kind; - if let PatKind::Binding(_, _, id, _) = &inner_pat.kind; + if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; + if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); + if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue(arms[0].body); if !contains_return_break_continue(arms[1].body); then { + let (capture_mut, capture_ref, capture_ref_mut) = match bind_annotation { + BindingAnnotation::Unannotated => (false, false, false), + BindingAnnotation::Mutable => (true, false, false), + BindingAnnotation::Ref => (false, true, false), + BindingAnnotation::RefMut => (false, false, true), + }; let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) = &arms[0].body.kind { if let &[] = &statements { @@ -189,7 +195,7 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } }); - let parens_around_option = match &let_body.kind { + let (parens_around_option, as_ref, as_mut, let_body) = match &let_body.kind { ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) @@ -197,13 +203,15 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - | ExprKind::Block(..) | ExprKind::Field(..) | ExprKind::Path(_) - => false, - _ => true, + => (false, capture_ref, capture_ref_mut, let_body), + ExprKind::Unary(UnOp::UnDeref, expr) => (false, capture_ref, capture_ref_mut, expr), + ExprKind::AddrOf(_, mutability, expr) => (false, mutability == &Mutability::Not, mutability == &Mutability::Mut, expr), + _ => (true, capture_ref, capture_ref_mut, let_body), }; Some(OptionIfLetElseOccurence { - option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }), + option: format!("{}{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }, if as_mut { ".as_mut()" } else if as_ref { ".as_ref()" } else { "" }), method_sugg: format!("{}", method_sugg), - some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), + some_expr: format!("|{}{}{}| {}", if false { "ref " } else { "" }, if capture_mut { "mut " } else { "" }, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), wrap_braces, }) diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 80b162714ac..695a460cc4e 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -11,8 +11,22 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } else { string.map_or(Some((false, "")), |x| Some((true, x))) } } -fn unop_bad(string: &Option<&str>) -> usize { - (*string).map_or(0, |s| s.len()) +fn unop_bad(string: &Option<&str>, mut num: Option) { + let _ = string.map_or(0, |s| s.len()); + let _ = num.as_ref().map_or(&0, |s| s); + let _ = num.as_mut().map_or(&mut 0, |s| { + *s += 1; + s + }); + let _ = num.as_ref().map_or(&0, |s| s); + let _ = num.map_or(0, |mut s| { + s += 1; + s + }); + let _ = num.as_mut().map_or(&mut 0, |s| { + *s += 1; + s + }); } fn longer_body(arg: Option) -> u32 { @@ -53,7 +67,7 @@ fn main() { let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); let _ = else_if_option(None); - let _ = unop_bad(&None); + unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7c43fbea373..6f9d506d347 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -19,12 +19,40 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } } -fn unop_bad(string: &Option<&str>) -> usize { - if let Some(s) = *string { +fn unop_bad(string: &Option<&str>, mut num: Option) { + let _ = if let Some(s) = *string { s.len() } else { 0 - } + }; + let _ = if let Some(s) = &num { + s + } else { + &0 + }; + let _ = if let Some(s) = &mut num { + *s += 1; + s + } else { + &mut 0 + }; + let _ = if let Some(ref s) = num { + s + } else { + &0 + }; + let _ = if let Some(mut s) = num { + s += 1; + s + } else { + 0 + }; + let _ = if let Some(ref mut s) = num { + *s += 1; + s + } else { + &mut 0 + }; } fn longer_body(arg: Option) -> u32 { @@ -69,7 +97,7 @@ fn main() { let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); let _ = else_if_option(None); - let _ = unop_bad(&None); + unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index b932fe59759..9240d3edb27 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -22,17 +22,100 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:23:5 + --> $DIR/option_if_let_else.rs:23:13 | -LL | / if let Some(s) = *string { +LL | let _ = if let Some(s) = *string { + | _____________^ LL | | s.len() LL | | } else { LL | | 0 -LL | | } - | |_____^ help: try: `(*string).map_or(0, |s| s.len())` +LL | | }; + | |_____^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:31:5 + --> $DIR/option_if_let_else.rs:28:13 + | +LL | let _ = if let Some(s) = &num { + | _____________^ +LL | | s +LL | | } else { +LL | | &0 +LL | | }; + | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:33:13 + | +LL | let _ = if let Some(s) = &mut num { + | _____________^ +LL | | *s += 1; +LL | | s +LL | | } else { +LL | | &mut 0 +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = num.as_mut().map_or(&mut 0, |s| { +LL | *s += 1; +LL | s +LL | }); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:39:13 + | +LL | let _ = if let Some(ref s) = num { + | _____________^ +LL | | s +LL | | } else { +LL | | &0 +LL | | }; + | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:44:13 + | +LL | let _ = if let Some(mut s) = num { + | _____________^ +LL | | s += 1; +LL | | s +LL | | } else { +LL | | 0 +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = num.map_or(0, |mut s| { +LL | s += 1; +LL | s +LL | }); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:50:13 + | +LL | let _ = if let Some(ref mut s) = num { + | _____________^ +LL | | *s += 1; +LL | | s +LL | | } else { +LL | | &mut 0 +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = num.as_mut().map_or(&mut 0, |s| { +LL | *s += 1; +LL | s +LL | }); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:59:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -51,7 +134,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:40:13 + --> $DIR/option_if_let_else.rs:68:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -74,10 +157,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:69:13 + --> $DIR/option_if_let_else.rs:97:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors From f73b455b99694fbc5ddec38317f705f546729db2 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 9 May 2020 20:44:56 -0700 Subject: [PATCH 0251/1110] Refactoring --- clippy_lints/src/option_if_let_else.rs | 170 +++++++++++++++---------- tests/ui/option_if_let_else.rs | 18 +-- tests/ui/option_if_let_else.stderr | 43 ++----- 3 files changed, 123 insertions(+), 108 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4e501f4ca02..208aa550765 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -69,17 +69,12 @@ declare_clippy_lint! { declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); -/// Returns true iff the given expression is the result of calling Result::ok +/// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { - if_chain! { - if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind; - if path.ident.name.to_ident_string() == "ok"; - if match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT); - then { - true - } else { - false - } + if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind { + path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) + } else { + false } } @@ -136,66 +131,108 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { recursive_visitor.seen_return_break_continue } +/// Extracts the body of a given arm. If the arm contains only an expression, +/// then it returns the expression. Otherwise, it returns the entire block +fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { + if let ExprKind::Block( + Block { + stmts: statements, + expr: Some(expr), + .. + }, + _, + ) = &arm.body.kind + { + if let [] = statements { + Some(&expr) + } else { + Some(&arm.body) + } + } else { + None + } +} + +/// If this is the else body of an if/else expression, then we need to wrap +/// it in curcly braces. Otherwise, we don't. +fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + if let Some(Expr { + kind: + ExprKind::Match( + _, + arms, + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + }, + ), + .. + }) = parent.expr + { + expr.hir_id == arms[1].body.hir_id + } else { + false + } + }) +} + +fn format_option_in_sugg( + cx: &LateContext<'_, '_>, + cond_expr: &Expr<'_>, + parens_around_option: bool, + as_ref: bool, + as_mut: bool, +) -> String { + format!( + "{}{}{}{}", + if parens_around_option { "(" } else { "" }, + Sugg::hir(cx, cond_expr, ".."), + if parens_around_option { ")" } else { "" }, + if as_mut { + ".as_mut()" + } else if as_ref { + ".as_ref()" + } else { + "" + } + ) +} + /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an OptionIfLetElseOccurence struct with details if +/// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; - // if type_is_option(cx, &cx.tables.expr_ty(let_body).kind); - if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already + if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue(arms[0].body); if !contains_return_break_continue(arms[1].body); then { - let (capture_mut, capture_ref, capture_ref_mut) = match bind_annotation { - BindingAnnotation::Unannotated => (false, false, false), - BindingAnnotation::Mutable => (true, false, false), - BindingAnnotation::Ref => (false, true, false), - BindingAnnotation::RefMut => (false, false, true), - }; - let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) - = &arms[0].body.kind { - if let &[] = &statements { - expr - } else { - &arms[0].body - } - } else { - return None; - }; - let (none_body, method_sugg) = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) - = &arms[1].body.kind { - if let &[] = &statements { - (expr, "map_or") - } else { - (&arms[1].body, "map_or_else") - } - } else { - return None; + let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; + let some_body = extract_body_from_arm(&arms[0])?; + let none_body = extract_body_from_arm(&arms[1])?; + let method_sugg = match &none_body.kind { + ExprKind::Block(..) => "map_or_else", + _ => "map_or", }; let capture_name = id.name.to_ident_string(); - let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { - if_chain! { - if let Some(Expr { kind: ExprKind::Match( - _, - arms, - MatchSource::IfDesugar{contains_else_clause: true} - | MatchSource::IfLetDesugar{contains_else_clause: true}), - .. } ) = parent.expr; - if expr.hir_id == arms[1].body.hir_id; - then { - true - } else { - false - } - } - }); - let (parens_around_option, as_ref, as_mut, let_body) = match &let_body.kind { + let wrap_braces = should_wrap_in_braces(cx, expr); + let (as_ref, as_mut) = match &cond_expr.kind { + ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), + ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), + _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), + }; + let parens_around_option = match &cond_expr.kind { + // Put parens around the option expression if not doing so might + // mess up the order of operations. ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) @@ -203,15 +240,20 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - | ExprKind::Block(..) | ExprKind::Field(..) | ExprKind::Path(_) - => (false, capture_ref, capture_ref_mut, let_body), - ExprKind::Unary(UnOp::UnDeref, expr) => (false, capture_ref, capture_ref_mut, expr), - ExprKind::AddrOf(_, mutability, expr) => (false, mutability == &Mutability::Not, mutability == &Mutability::Mut, expr), - _ => (true, capture_ref, capture_ref_mut, let_body), + | ExprKind::Unary(UnOp::UnDeref, _) + | ExprKind::AddrOf(..) + => false, + _ => true, + }; + let cond_expr = match &cond_expr.kind { + // Pointer dereferencing happens automatically, so we can omit it in the suggestion + ExprKind::Unary(UnOp::UnDeref, expr)|ExprKind::AddrOf(_, _, expr) => expr, + _ => cond_expr, }; Some(OptionIfLetElseOccurence { - option: format!("{}{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }, if as_mut { ".as_mut()" } else if as_ref { ".as_ref()" } else { "" }), - method_sugg: format!("{}", method_sugg), - some_expr: format!("|{}{}{}| {}", if false { "ref " } else { "" }, if capture_mut { "mut " } else { "" }, capture_name, Sugg::hir(cx, some_body, "..")), + option: format_option_in_sugg(cx, cond_expr, parens_around_option, as_ref, as_mut), + method_sugg: method_sugg.to_string(), + some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), wrap_braces, }) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 6f9d506d347..dee80d26bd9 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -20,27 +20,15 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } fn unop_bad(string: &Option<&str>, mut num: Option) { - let _ = if let Some(s) = *string { - s.len() - } else { - 0 - }; - let _ = if let Some(s) = &num { - s - } else { - &0 - }; + let _ = if let Some(s) = *string { s.len() } else { 0 }; + let _ = if let Some(s) = &num { s } else { &0 }; let _ = if let Some(s) = &mut num { *s += 1; s } else { &mut 0 }; - let _ = if let Some(ref s) = num { - s - } else { - &0 - }; + let _ = if let Some(ref s) = num { s } else { &0 }; let _ = if let Some(mut s) = num { s += 1; s diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 9240d3edb27..7005850efaf 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -24,27 +24,17 @@ LL | | } error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:23:13 | -LL | let _ = if let Some(s) = *string { - | _____________^ -LL | | s.len() -LL | | } else { -LL | | 0 -LL | | }; - | |_____^ help: try: `string.map_or(0, |s| s.len())` +LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:28:13 + --> $DIR/option_if_let_else.rs:24:13 | -LL | let _ = if let Some(s) = &num { - | _____________^ -LL | | s -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` +LL | let _ = if let Some(s) = &num { s } else { &0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -64,18 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:31:13 | -LL | let _ = if let Some(ref s) = num { - | _____________^ -LL | | s -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` +LL | let _ = if let Some(ref s) = num { s } else { &0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:44:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -95,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:50:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -115,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:59:5 + --> $DIR/option_if_let_else.rs:47:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -134,7 +119,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:68:13 + --> $DIR/option_if_let_else.rs:56:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -157,7 +142,7 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:97:13 + --> $DIR/option_if_let_else.rs:85:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` From 7c4de9d3dee3a7e16118df5c1cd2080af7350d98 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 11 May 2020 17:52:47 -0700 Subject: [PATCH 0252/1110] Refactoring pt. 2 --- clippy_lints/src/option_if_let_else.rs | 33 ++++---------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 208aa550765..6e8d7515671 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -179,18 +179,10 @@ fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } -fn format_option_in_sugg( - cx: &LateContext<'_, '_>, - cond_expr: &Expr<'_>, - parens_around_option: bool, - as_ref: bool, - as_mut: bool, -) -> String { +fn format_option_in_sugg(cx: &LateContext<'_, '_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( - "{}{}{}{}", - if parens_around_option { "(" } else { "" }, - Sugg::hir(cx, cond_expr, ".."), - if parens_around_option { ")" } else { "" }, + "{}{}", + Sugg::hir(cx, cond_expr, "..").maybe_par(), if as_mut { ".as_mut()" } else if as_ref { @@ -230,28 +222,13 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), }; - let parens_around_option = match &cond_expr.kind { - // Put parens around the option expression if not doing so might - // mess up the order of operations. - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - | ExprKind::Block(..) - | ExprKind::Field(..) - | ExprKind::Path(_) - | ExprKind::Unary(UnOp::UnDeref, _) - | ExprKind::AddrOf(..) - => false, - _ => true, - }; let cond_expr = match &cond_expr.kind { // Pointer dereferencing happens automatically, so we can omit it in the suggestion - ExprKind::Unary(UnOp::UnDeref, expr)|ExprKind::AddrOf(_, _, expr) => expr, + ExprKind::Unary(UnOp::UnDeref, expr) | ExprKind::AddrOf(_, _, expr) => expr, _ => cond_expr, }; Some(OptionIfLetElseOccurence { - option: format_option_in_sugg(cx, cond_expr, parens_around_option, as_ref, as_mut), + option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), From 5e20475e47d3db1d32a7649a7c3a107caba32a14 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:34:10 -0700 Subject: [PATCH 0253/1110] Don't lint if contains a macro --- clippy_lints/src/option_if_let_else.rs | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 6e8d7515671..be0c44cae34 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -10,8 +10,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::marker::PhantomData; - declare_clippy_lint! { /// **What it does:** /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more @@ -88,19 +86,17 @@ struct OptionIfLetElseOccurence { wrap_braces: bool, } -struct ReturnBreakContinueVisitor<'tcx> { +struct ReturnBreakContinueMacroVisitor { seen_return_break_continue: bool, - phantom_data: PhantomData<&'tcx bool>, } -impl<'tcx> ReturnBreakContinueVisitor<'tcx> { - fn new() -> ReturnBreakContinueVisitor<'tcx> { - ReturnBreakContinueVisitor { +impl ReturnBreakContinueMacroVisitor { + fn new() -> ReturnBreakContinueMacroVisitor { + ReturnBreakContinueMacroVisitor { seen_return_break_continue: false, - phantom_data: PhantomData, } } } -impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -119,14 +115,18 @@ impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { // desugaring, as this will detect a break if there's a while loop // or a for loop inside the expression. _ => { - rustc_hir::intravisit::walk_expr(self, ex); + if utils::in_macro(ex.span) { + self.seen_return_break_continue = true; + } else { + rustc_hir::intravisit::walk_expr(self, ex); + } }, } } } -fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { - let mut recursive_visitor: ReturnBreakContinueVisitor<'tcx> = ReturnBreakContinueVisitor::new(); +fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { + let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); recursive_visitor.visit_expr(expression); recursive_visitor.seen_return_break_continue } @@ -205,8 +205,8 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue(arms[0].body); - if !contains_return_break_continue(arms[1].body); + if !contains_return_break_continue_macro(arms[0].body); + if !contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; From 5150277a4f765bb113e163adc7eb495dcbb57129 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:37:21 -0700 Subject: [PATCH 0254/1110] Used clippy to clean itself --- clippy_lints/src/attrs.rs | 14 ++----- clippy_lints/src/if_let_mutex.rs | 8 +--- clippy_lints/src/len_zero.rs | 8 +--- clippy_lints/src/literal_representation.rs | 7 ++-- clippy_lints/src/loops.rs | 26 +++--------- clippy_lints/src/methods/mod.rs | 6 +-- .../src/methods/unnecessary_filter_map.rs | 6 +-- clippy_lints/src/minmax.rs | 21 +++++----- clippy_lints/src/misc.rs | 8 +--- clippy_lints/src/option_if_let_else.rs | 4 +- clippy_lints/src/returns.rs | 9 +---- clippy_lints/src/shadow.rs | 10 ++--- clippy_lints/src/types.rs | 12 +++--- clippy_lints/src/use_self.rs | 8 +--- clippy_lints/src/utils/attrs.rs | 14 +++---- clippy_lints/src/utils/mod.rs | 40 ++++--------------- clippy_lints/src/utils/numeric_literal.rs | 6 +-- clippy_lints/src/utils/sugg.rs | 8 +--- clippy_lints/src/write.rs | 6 +-- 19 files changed, 67 insertions(+), 154 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 2d0855f6895..cfad9d79f2b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -481,15 +481,11 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { } fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { - if let Some(stmt) = block.stmts.first() { - match &stmt.kind { + block.stmts.first().map_or(block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr), _ => false, - } - } else { - block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)) - } + }) } fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool { @@ -499,11 +495,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: & ExprKind::Ret(None) | ExprKind::Break(_, None) => false, ExprKind::Call(path_expr, _) => { if let ExprKind::Path(qpath) = &path_expr.kind { - if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { - !match_def_path(cx, fun_id, &paths::BEGIN_PANIC) - } else { - true - } + tables.qpath_res(qpath, path_expr.hir_id).opt_def_id().map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)) } else { true } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index f911cb68ea5..7e44618e90e 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -135,13 +135,9 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } } -impl<'tcx> ArmVisitor<'_, 'tcx> { +impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { - if let Some(arm_mutex) = self.found_mutex { - SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) - } else { - false - } + self.found_mutex.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 26d96428771..f57fa830adc 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -303,14 +303,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => { - if let Some(principal) = tt.principal() { - cx.tcx + tt.principal().map_or(false, |principal| cx.tcx .associated_items(principal.def_id()) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) - } else { - false - } + .any(|item| is_is_empty(cx, &item))) }, ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7ba43562d7d..ea2e23bd3a1 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,10 +264,11 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') - } else if let Some(fraction) = &mut num_lit.fraction { - (fraction, &["32", "64"][..], 'f') } else { - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + num_lit.fraction.as_mut().map_or( + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), + |fraction| (fraction, &["32", "64"][..], 'f') + ) }; let mut split = part.rsplit('_'); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d821b513484..8d48f39a045 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -687,11 +687,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } }, ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => { - if let Some(ref e) = *e { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) - } else { - NeverLoopResult::AlwaysBreak - } + e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)) }, ExprKind::InlineAsm(ref asm) => asm .operands @@ -1882,11 +1878,7 @@ fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc match ty.kind { ty::Array(_, n) => { - if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) { - (0..=32).contains(&val) - } else { - false - } + n.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |val| (0..=32).contains(&val)) }, _ => false, } @@ -1899,11 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< return None; } if let StmtKind::Local(ref local) = block.stmts[0].kind { - if let Some(expr) = local.init { - Some(expr) - } else { - None - } + local.init.map(|expr| expr) } else { None } @@ -2023,15 +2011,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if let PatKind::Binding(.., ident, _) = local.pat.kind { self.name = Some(ident.name); - self.state = if let Some(ref init) = local.init { - if is_integer_const(&self.cx, init, 0) { + self.state = local.init.as_ref().map_or(VarState::Declared, |init| if is_integer_const(&self.cx, init, 0) { VarState::Warn } else { VarState::Declared - } - } else { - VarState::Declared - } + }) } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 160304865c5..ddad16e163e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2461,11 +2461,7 @@ fn derefs_to_slice<'tcx>( ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), ty::Array(_, size) => { - if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) { - size < 32 - } else { - false - } + size.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |size| size < 32) }, ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index fdcba110542..97909c97fc7 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -78,11 +78,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::Block(ref block, _) => { - if let Some(expr) = &block.expr { - check_expression(cx, arg_id, &expr) - } else { - (false, false) - } + block.expr.as_ref().map_or((false, false), |expr| check_expression(cx, arg_id, &expr)) }, hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 0a2d577396a..a3d1a500aa7 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -86,16 +86,19 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt if args.len() != 2 { return None; } - if let Some(c) = constant_simple(cx, cx.tables(), &args[0]) { - if constant_simple(cx, cx.tables(), &args[1]).is_none() { - // otherwise ignore - Some((m, c, &args[1])) + constant_simple(cx, cx.tables, &args[0]).map_or_else( + || if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { + Some((m, c, &args[0])) } else { None + }, + |c| { + if constant_simple(cx, cx.tables, &args[1]).is_none() { + // otherwise ignore + Some((c, &args[1])) + } else { + None + } } - } else if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { - Some((m, c, &args[0])) - } else { - None - } + ).map(|(c, arg)| (m, c, arg)) } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 6a256627bd1..5b0f9d6e3ec 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -682,16 +682,12 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: /// `unused_variables`'s idea /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, expr) { - match parent.kind { + get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind { ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => { SpanlessEq::new(cx).eq_expr(rhs, expr) }, _ => is_used(cx, parent), - } - } else { - true - } + }) } /// Tests whether an expression is in a macro expansion (e.g., something diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index be0c44cae34..b6d08b8ae17 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -37,6 +37,7 @@ declare_clippy_lint! { /// /// ```rust /// # let optional: Option = Some(0); + /// # fn do_complicated_function() -> u32 { 5 }; /// let _ = if let Some(foo) = optional { /// foo /// } else { @@ -54,9 +55,10 @@ declare_clippy_lint! { /// /// ```rust /// # let optional: Option = Some(0); + /// # fn do_complicated_function() -> u32 { 5 }; /// let _ = optional.map_or(5, |foo| foo); /// let _ = optional.map_or_else(||{ - /// let y = do_complicated_function; + /// let y = do_complicated_function(); /// y*y /// }, |foo| foo); /// ``` diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c939744173..4d54e3117fa 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -259,15 +259,10 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - ( + fn_source.rfind("->").map_or((ty.span, Applicability::MaybeIncorrect), |rpos| ( ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, - ) - } else { - (ty.span, Applicability::MaybeIncorrect) - } + )) } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 7da47ee4ff9..de94fb87147 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -164,15 +164,11 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & } fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { - let var_ty = cx.tables().node_type_opt(pat_id); - if let Some(var_ty) = var_ty { - match var_ty.kind { + let var_ty = cx.tables.node_type_opt(pat_id); + var_ty.map_or(false, |var_ty| match var_ty.kind { ty::Adt(..) => false, _ => true, - } - } else { - false - } + }) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b1345f0de5e..df87c1b9802 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1205,16 +1205,14 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; let opt = snippet_opt(cx, op.span); - let sugg = if let Some(ref snip) = opt { - if should_strip_parens(op, snip) { + let sugg = opt.as_ref().map_or_else(|| { + applicability = Applicability::HasPlaceholders; + ".." + }, |snip| if should_strip_parens(op, snip) { &snip[1..snip.len() - 1] } else { snip.as_str() - } - } else { - applicability = Applicability::HasPlaceholders; - ".." - }; + }); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f85db1e2006..eac7ae2358e 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -167,14 +167,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind; then { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - let should_check = if let Some(ref params) = *parameters { - !params.parenthesized && !params.args.iter().any(|arg| match arg { + let should_check = parameters.as_ref().map_or(true, |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { GenericArg::Lifetime(_) => true, _ => false, - }) - } else { - true - }; + })); if should_check { let visitor = &mut UseSelfVisitor { diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 4dcf6c105ec..2d72f9c3fe1 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -65,8 +65,7 @@ pub fn get_attr<'a>( }; let attr_segments = &attr.path.segments; if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { - if let Some(deprecation_status) = - BUILTIN_ATTRIBUTES + BUILTIN_ATTRIBUTES .iter() .find_map(|(builtin_name, deprecation_status)| { if *builtin_name == attr_segments[1].ident.to_string() { @@ -74,8 +73,10 @@ pub fn get_attr<'a>( } else { None } - }) - { + }).map_or_else(|| { + sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + false + }, |deprecation_status| { let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { @@ -97,10 +98,7 @@ pub fn get_attr<'a>( attr_segments[1].ident.to_string() == name }, } - } else { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); - false - } + }) } else { false } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 99ba7d64331..6f23e968006 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -153,11 +153,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap(); let trt_id = cx.tcx.trait_of_item(def_id); - if let Some(trt_id) = trt_id { - match_def_path(cx, trt_id, path) - } else { - false - } + trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } /// Checks if an expression references a variable of the given name. @@ -600,21 +596,13 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// // ^^^^^^^^^^ /// ``` pub fn first_line_of_span(cx: &T, span: Span) -> Span { - if let Some(first_char_pos) = first_char_in_first_line(cx, span) { - span.with_lo(first_char_pos) - } else { - span - } + first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } fn first_char_in_first_line(cx: &T, span: Span) -> Option { let line_span = line_span(cx, span); - if let Some(snip) = snippet_opt(cx, line_span) { - snip.find(|c: char| !c.is_whitespace()) - .map(|pos| line_span.lo() + BytePos::from_usize(pos)) - } else { - None - } + snippet_opt(cx, line_span).and_then(|snip| snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos))) } /// Returns the indentation of the line of a span @@ -626,11 +614,7 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Option { - if let Some(snip) = snippet_opt(cx, line_span(cx, span)) { - snip.find(|c: char| !c.is_whitespace()) - } else { - None - } + snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Extends the span to the beginning of the spans line, incl. whitespaces. @@ -738,8 +722,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio let enclosing_node = map .get_enclosing_scope(hir_id) .and_then(|enclosing_id| map.find(enclosing_id)); - if let Some(node) = enclosing_node { - match node { + enclosing_node.and_then(|node| match node { Node::Block(block) => Some(block), Node::Item(&Item { kind: ItemKind::Fn(_, _, eid), @@ -753,10 +736,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio _ => None, }, _ => None, - } - } else { - None - } + }) } /// Returns the base type for HIR references and pointers. @@ -1328,11 +1308,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - if let Some(did) = did { - must_use_attr(&cx.tcx.get_attrs(did)).is_some() - } else { - false - } + did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 99413153d49..7a79741b30b 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -200,12 +200,10 @@ impl<'a> NumericLiteral<'a> { fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) { debug_assert!(lit_kind.is_numeric()); - if let Some(suffix_length) = lit_suffix_length(lit_kind) { + lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| { let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length); (unsuffixed, Some(suffix)) - } else { - (src, None) - } + }) } fn lit_suffix_length(lit_kind: &LitKind) -> Option { diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 8fc97f2fd64..20bea3cbabe 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -492,8 +492,7 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp { /// before it on its line. fn indentation(cx: &T, span: Span) -> Option { let lo = cx.sess().source_map().lookup_char_pos(span.lo()); - if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) { - if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { + lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */).and_then(|line| if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { // We can mix char and byte positions here because we only consider `[ \t]`. if lo.col == CharPos(pos) { Some(line[..pos].into()) @@ -502,10 +501,7 @@ fn indentation(cx: &T, span: Span) -> Option { } } else { None - } - } else { - None - } + }) } /// Convenience extension trait for `DiagnosticBuilder`. diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 732f4b28e06..3b10b7b82a0 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -297,12 +297,10 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = if let Some(e) = expr { - snippet_with_applicability(cx, e.span, "v", &mut applicability) - } else { + let suggestion = expr.map_or_else(|| { applicability = Applicability::HasPlaceholders; Cow::Borrowed("v") - }; + }, |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable)); span_lint_and_sugg( cx, From 93f0f5d37b45c648aebf87fe7e7379599ba3e726 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 12 Jun 2020 08:32:38 -0700 Subject: [PATCH 0255/1110] Last few tweaks --- CHANGELOG.md | 1 - clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/minmax.rs | 3 +-- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/returns.rs | 9 +++++++-- src/lintlist/mod.rs | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..01c0e4b0302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1491,7 +1491,6 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero -[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cd91e7ceb32..fe34e4390d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1161,6 +1161,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_continue::NEEDLESS_CONTINUE), LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), @@ -1372,7 +1373,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), @@ -1521,7 +1521,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index a3d1a500aa7..6ec7f8cae5d 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -99,6 +99,5 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt } else { None } - } - ).map(|(c, arg)| (m, c, arg)) + }) } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index b6d08b8ae17..c877cc6fa8b 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { /// }, |foo| foo); /// ``` pub OPTION_IF_LET_ELSE, - style, + pedantic, "reimplementation of Option::map_or" } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4d54e3117fa..3c939744173 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -259,10 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source.rfind("->").map_or((ty.span, Applicability::MaybeIncorrect), |rpos| ( + if let Some(rpos) = fn_source.rfind("->") { + #[allow(clippy::cast_possible_truncation)] + ( ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, - )) + ) + } else { + (ty.span, Applicability::MaybeIncorrect) + } } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b499d565fa7..e681f47f949 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1622,7 +1622,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "option_if_let_else", - group: "style", + group: "pedantic", desc: "reimplementation of Option::map_or", deprecation: None, module: "option_if_let_else", From ccb999851aaa4d3e9cd97110bd6523a6c3df46fd Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 25 Jun 2020 11:39:58 -0700 Subject: [PATCH 0256/1110] Fix compile error from library change --- CHANGELOG.md | 1 + clippy_lints/src/option_if_let_else.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c0e4b0302..1a081bb85fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1491,6 +1491,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c877cc6fa8b..7e299dd5fd9 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -71,7 +71,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { - if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind { + if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) } else { false From 6ce981225b73d6c3514b0abb759a5282521a17d9 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 25 Jun 2020 11:58:47 -0700 Subject: [PATCH 0257/1110] Clean existing lint code to match new lint --- clippy_lints/src/attrs.rs | 14 ++-- clippy_lints/src/if_let_mutex.rs | 5 +- clippy_lints/src/len_zero.rs | 12 ++-- clippy_lints/src/literal_representation.rs | 10 +-- clippy_lints/src/loops.rs | 20 +++--- clippy_lints/src/methods/mod.rs | 6 +- .../src/methods/unnecessary_filter_map.rs | 7 +- clippy_lints/src/minmax.rs | 2 +- clippy_lints/src/misc.rs | 8 +-- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/returns.rs | 18 ++--- clippy_lints/src/shadow.rs | 6 +- clippy_lints/src/types.rs | 21 +++--- clippy_lints/src/use_self.rs | 7 +- clippy_lints/src/utils/attrs.rs | 67 ++++++++++--------- clippy_lints/src/utils/mod.rs | 32 ++++----- clippy_lints/src/utils/sugg.rs | 16 +++-- clippy_lints/src/write.rs | 11 +-- 18 files changed, 148 insertions(+), 116 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index cfad9d79f2b..c4397560d7d 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -480,12 +480,15 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { } } -fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { - block.stmts.first().map_or(block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), |stmt| match &stmt.kind { +fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { + block.stmts.first().map_or( + block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), + |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr), _ => false, - }) + }, + ) } fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool { @@ -495,7 +498,10 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: & ExprKind::Ret(None) | ExprKind::Break(_, None) => false, ExprKind::Call(path_expr, _) => { if let ExprKind::Path(qpath) = &path_expr.kind { - tables.qpath_res(qpath, path_expr.hir_id).opt_def_id().map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)) + tables + .qpath_res(qpath, path_expr.hir_id) + .opt_def_id() + .map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)) } else { true } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 7e44618e90e..5426e14ead5 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -136,8 +136,9 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { - self.found_mutex.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) + fn same_mutex(&self, cx: &LateContext<'_, '_>, op_mutex: &Expr<'_>) -> bool { + self.found_mutex + .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f57fa830adc..1b09328ceab 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -302,12 +302,12 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr)); match ty.kind { - ty::Dynamic(ref tt, ..) => { - tt.principal().map_or(false, |principal| cx.tcx - .associated_items(principal.def_id()) - .in_definition_order() - .any(|item| is_is_empty(cx, &item))) - }, + ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + cx.tcx + .associated_items(principal.def_id()) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did), ty::Array(..) | ty::Slice(..) | ty::Str => true, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index ea2e23bd3a1..a36fdca5d5d 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -265,10 +265,12 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') } else { - num_lit.fraction.as_mut().map_or( - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), - |fraction| (fraction, &["32", "64"][..], 'f') - ) + num_lit + .fraction + .as_mut() + .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| { + (fraction, &["32", "64"][..], 'f') + }) }; let mut split = part.rsplit('_'); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8d48f39a045..b803d753b6d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -686,9 +686,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, - ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => { - e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)) - }, + ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { + combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + }), ExprKind::InlineAsm(ref asm) => asm .operands .iter() @@ -1877,9 +1877,9 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc match ty.kind { - ty::Array(_, n) => { - n.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |val| (0..=32).contains(&val)) - }, + ty::Array(_, n) => n + .try_eval_usize(cx.tcx, cx.param_env) + .map_or(false, |val| (0..=32).contains(&val)), _ => false, } } @@ -1891,7 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< return None; } if let StmtKind::Local(ref local) = block.stmts[0].kind { - local.init.map(|expr| expr) + local.init //.map(|expr| expr) } else { None } @@ -2011,11 +2011,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if let PatKind::Binding(.., ident, _) = local.pat.kind { self.name = Some(ident.name); - self.state = local.init.as_ref().map_or(VarState::Declared, |init| if is_integer_const(&self.cx, init, 0) { + self.state = local.init.as_ref().map_or(VarState::Declared, |init| { + if is_integer_const(&self.cx, init, 0) { VarState::Warn } else { VarState::Declared - }) + } + }) } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ddad16e163e..f1c8894c0ee 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2460,9 +2460,9 @@ fn derefs_to_slice<'tcx>( ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), - ty::Array(_, size) => { - size.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |size| size < 32) - }, + ty::Array(_, size) => size + .try_eval_usize(cx.tcx, cx.param_env) + .map_or(false, |size| size < 32), ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 97909c97fc7..75e123eb593 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -77,9 +77,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, - hir::ExprKind::Block(ref block, _) => { - block.expr.as_ref().map_or((false, false), |expr| check_expression(cx, arg_id, &expr)) - }, + hir::ExprKind::Block(ref block, _) => block + .expr + .as_ref() + .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)), hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 6ec7f8cae5d..5eb8398d68e 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MinMaxPass { } } -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] enum MinMax { Min, Max, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 5b0f9d6e3ec..3d4225f36a7 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -683,11 +683,9 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind { - ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => { - SpanlessEq::new(cx).eq_expr(rhs, expr) - }, - _ => is_used(cx, parent), - }) + ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), + _ => is_used(cx, parent), + }) } /// Tests whether an expression is in a macro expansion (e.g., something diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 7e299dd5fd9..ab9ea76a838 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -260,7 +260,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { detection.some_expr, if detection.wrap_braces { " }" } else { "" }, ), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c939744173..faef7e724dd 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -259,15 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - ( - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - } else { - (ty.span, Applicability::MaybeIncorrect) - } + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index de94fb87147..f16db2df3a9 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -166,9 +166,9 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { let var_ty = cx.tables.node_type_opt(pat_id); var_ty.map_or(false, |var_ty| match var_ty.kind { - ty::Adt(..) => false, - _ => true, - }) + ty::Adt(..) => false, + _ => true, + }) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index df87c1b9802..d6f31a99bb3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1205,14 +1205,19 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; let opt = snippet_opt(cx, op.span); - let sugg = opt.as_ref().map_or_else(|| { - applicability = Applicability::HasPlaceholders; - ".." - }, |snip| if should_strip_parens(op, snip) { - &snip[1..snip.len() - 1] - } else { - snip.as_str() - }); + let sugg = opt.as_ref().map_or_else( + || { + applicability = Applicability::HasPlaceholders; + ".." + }, + |snip| { + if should_strip_parens(op, snip) { + &snip[1..snip.len() - 1] + } else { + snip.as_str() + } + }, + ); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index eac7ae2358e..39a8c020872 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -167,10 +167,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind; then { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - let should_check = parameters.as_ref().map_or(true, |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { + let should_check = parameters.as_ref().map_or( + true, + |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { GenericArg::Lifetime(_) => true, _ => false, - })); + }) + ); if should_check { let visitor = &mut UseSelfVisitor { diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 2d72f9c3fe1..4bb4b087c55 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -66,39 +66,44 @@ pub fn get_attr<'a>( let attr_segments = &attr.path.segments; if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { BUILTIN_ATTRIBUTES - .iter() - .find_map(|(builtin_name, deprecation_status)| { - if *builtin_name == attr_segments[1].ident.to_string() { - Some(deprecation_status) - } else { - None + .iter() + .find_map(|(builtin_name, deprecation_status)| { + if *builtin_name == attr_segments[1].ident.to_string() { + Some(deprecation_status) + } else { + None + } + }) + .map_or_else( + || { + sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + false + }, + |deprecation_status| { + let mut diag = + sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + match *deprecation_status { + DeprecationStatus::Deprecated => { + diag.emit(); + false + }, + DeprecationStatus::Replaced(new_name) => { + diag.span_suggestion( + attr_segments[1].ident.span, + "consider using", + new_name.to_string(), + Applicability::MachineApplicable, + ); + diag.emit(); + false + }, + DeprecationStatus::None => { + diag.cancel(); + attr_segments[1].ident.to_string() == name + }, } - }).map_or_else(|| { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); - false - }, |deprecation_status| { - let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); - match *deprecation_status { - DeprecationStatus::Deprecated => { - diag.emit(); - false }, - DeprecationStatus::Replaced(new_name) => { - diag.span_suggestion( - attr_segments[1].ident.span, - "consider using", - new_name.to_string(), - Applicability::MachineApplicable, - ); - diag.emit(); - false - }, - DeprecationStatus::None => { - diag.cancel(); - attr_segments[1].ident.to_string() == name - }, - } - }) + ) } else { false } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 6f23e968006..3a3b79925ff 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -601,8 +601,10 @@ pub fn first_line_of_span(cx: &T, span: Span) -> Span { fn first_char_in_first_line(cx: &T, span: Span) -> Option { let line_span = line_span(cx, span); - snippet_opt(cx, line_span).and_then(|snip| snip.find(|c: char| !c.is_whitespace()) - .map(|pos| line_span.lo() + BytePos::from_usize(pos))) + snippet_opt(cx, line_span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos)) + }) } /// Returns the indentation of the line of a span @@ -723,20 +725,20 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio .get_enclosing_scope(hir_id) .and_then(|enclosing_id| map.find(enclosing_id)); enclosing_node.and_then(|node| match node { - Node::Block(block) => Some(block), - Node::Item(&Item { - kind: ItemKind::Fn(_, _, eid), - .. - }) - | Node::ImplItem(&ImplItem { - kind: ImplItemKind::Fn(_, eid), - .. - }) => match cx.tcx.hir().body(eid).value.kind { - ExprKind::Block(ref block, _) => Some(block), - _ => None, - }, - _ => None, + Node::Block(block) => Some(block), + Node::Item(&Item { + kind: ItemKind::Fn(_, _, eid), + .. }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Fn(_, eid), + .. + }) => match cx.tcx.hir().body(eid).value.kind { + ExprKind::Block(ref block, _) => Some(block), + _ => None, + }, + _ => None, + }) } /// Returns the base type for HIR references and pointers. diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 20bea3cbabe..0ac7714fbeb 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -492,15 +492,19 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp { /// before it on its line. fn indentation(cx: &T, span: Span) -> Option { let lo = cx.sess().source_map().lookup_char_pos(span.lo()); - lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */).and_then(|line| if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { - // We can mix char and byte positions here because we only consider `[ \t]`. - if lo.col == CharPos(pos) { - Some(line[..pos].into()) + lo.file + .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) + .and_then(|line| { + if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { + // We can mix char and byte positions here because we only consider `[ \t]`. + if lo.col == CharPos(pos) { + Some(line[..pos].into()) + } else { + None + } } else { None } - } else { - None }) } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 3b10b7b82a0..063f94582b9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -297,10 +297,13 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else(|| { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable)); + let suggestion = expr.map_or_else( + || { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }, + |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable), + ); span_lint_and_sugg( cx, From 6e2d55c8db04a80f9c217f2089066b29302f62a9 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 27 Jun 2020 16:55:47 -0700 Subject: [PATCH 0258/1110] Update compile-test to follow new lint --- tests/compile-test.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 99505fc6b29..eb6d495acbe 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -12,19 +12,11 @@ use std::path::{Path, PathBuf}; mod cargo; fn host_lib() -> PathBuf { - if let Some(path) = option_env!("HOST_LIBS") { - PathBuf::from(path) - } else { - cargo::CARGO_TARGET_DIR.join(env!("PROFILE")) - } + option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } fn clippy_driver_path() -> PathBuf { - if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") { - PathBuf::from(path) - } else { - cargo::TARGET_LIB.join("clippy-driver") - } + option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from) } // When we'll want to use `extern crate ..` for a dependency that is used From 1c32263176d95ae47928d1955e44a4315ffcea2d Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 1 Jul 2020 11:41:11 -0700 Subject: [PATCH 0259/1110] Formatted updates to lints --- clippy_lints/src/minmax.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 5eb8398d68e..2e5f5f10f4b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -87,10 +87,12 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt return None; } constant_simple(cx, cx.tables, &args[0]).map_or_else( - || if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { - Some((m, c, &args[0])) - } else { - None + || { + if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { + Some((m, c, &args[0])) + } else { + None + } }, |c| { if constant_simple(cx, cx.tables, &args[1]).is_none() { @@ -99,5 +101,6 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt } else { None } - }) + }, + ) } From c8f700ea697f74ef8f86891b050c859cf457e3ab Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 3 Jul 2020 20:28:40 -0700 Subject: [PATCH 0260/1110] Fixed compile errors --- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/if_let_mutex.rs | 2 +- clippy_lints/src/minmax.rs | 14 ++++---------- clippy_lints/src/option_if_let_else.rs | 14 +++++++------- clippy_lints/src/shadow.rs | 2 +- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c4397560d7d..d68d0d8ccf5 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -480,7 +480,7 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { } } -fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { +fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { block.stmts.first().map_or( block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), |stmt| match &stmt.kind { diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 5426e14ead5..fbd2eeacc6e 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -136,7 +136,7 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn same_mutex(&self, cx: &LateContext<'_, '_>, op_mutex: &Expr<'_>) -> bool { + fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { self.found_mutex .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 2e5f5f10f4b..c8aa98d3489 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -86,18 +86,12 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt if args.len() != 2 { return None; } - constant_simple(cx, cx.tables, &args[0]).map_or_else( - || { - if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { - Some((m, c, &args[0])) - } else { - None - } - }, + constant_simple(cx, cx.tables(), &args[0]).map_or_else( + || constant_simple(cx, cx.tables(), &args[1]).map(|c| (m, c, &args[0])), |c| { - if constant_simple(cx, cx.tables, &args[1]).is_none() { + if constant_simple(cx, cx.tables(), &args[1]).is_none() { // otherwise ignore - Some((c, &args[1])) + Some((m, c, &args[1])) } else { None } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index ab9ea76a838..8dbe58763bf 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -70,9 +70,9 @@ declare_clippy_lint! { declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` -fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { +fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { - path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) + path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables().expr_ty(&receiver), &paths::RESULT) } else { false } @@ -157,7 +157,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { /// If this is the else body of an if/else expression, then we need to wrap /// it in curcly braces. Otherwise, we don't. -fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { +fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if let Some(Expr { kind: @@ -181,7 +181,7 @@ fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } -fn format_option_in_sugg(cx: &LateContext<'_, '_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { +fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", Sugg::hir(cx, cond_expr, "..").maybe_par(), @@ -198,7 +198,7 @@ fn format_option_in_sugg(cx: &LateContext<'_, '_>, cond_expr: &Expr<'_>, as_ref: /// If this expression is the option if let/else construct we're detecting, then /// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { +fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; @@ -242,8 +242,8 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { +impl<'a> LateLintPass<'a> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) { if let Some(detection) = detect_option_if_let_else(cx, expr) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index f16db2df3a9..4cdff63f118 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -164,7 +164,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & } fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { - let var_ty = cx.tables.node_type_opt(pat_id); + let var_ty = cx.tables().node_type_opt(pat_id); var_ty.map_or(false, |var_ty| match var_ty.kind { ty::Adt(..) => false, _ => true, From a6f1af75d71fcf8e029b78142370e7563798c503 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Fri, 3 Apr 2020 13:58:52 -0300 Subject: [PATCH 0261/1110] Lint for x.powi(2) => x * x --- clippy_lints/src/floating_point_arithmetic.rs | 27 ++++++++++++++++++- tests/ui/floating_point_log.fixed | 10 +++---- tests/ui/floating_point_log.rs | 10 +++---- tests/ui/floating_point_log.stderr | 20 +++++++------- tests/ui/floating_point_powf.fixed | 4 +-- tests/ui/floating_point_powf.rs | 4 +-- tests/ui/floating_point_powf.stderr | 8 +++--- tests/ui/floating_point_powi.fixed | 12 +++++++++ tests/ui/floating_point_powi.rs | 12 +++++++++ tests/ui/floating_point_powi.stderr | 16 +++++++++++ 10 files changed, 94 insertions(+), 29 deletions(-) create mode 100644 tests/ui/floating_point_powi.fixed create mode 100644 tests/ui/floating_point_powi.rs create mode 100644 tests/ui/floating_point_powi.stderr diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 4efd0689267..e3ee4296119 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,6 +1,6 @@ use crate::consts::{ constant, constant_simple, Constant, - Constant::{F32, F64}, + Constant::{Int, F32, F64}, }; use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use if_chain::if_chain; @@ -293,6 +293,30 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } +fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + // Check argument + if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + let (lint, help, suggestion) = match value { + Int(2) => ( + IMPRECISE_FLOPS, + "square can be computed more accurately", + format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), + ), + _ => return, + }; + + span_lint_and_sugg( + cx, + lint, + expr.span, + help, + "consider using", + suggestion, + Applicability::MachineApplicable, + ); + } +} + // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -489,6 +513,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { "ln" => check_ln1p(cx, expr, args), "log" => check_log_base(cx, expr, args), "powf" => check_powf(cx, expr, args), + "powi" => check_powi(cx, expr, args), _ => {}, } } diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 42c5e5d2bae..7dc7ee94aff 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -25,11 +25,11 @@ fn check_ln1p() { let _ = 2.0f32.ln_1p(); let _ = x.ln_1p(); let _ = (x / 2.0).ln_1p(); - let _ = x.powi(2).ln_1p(); - let _ = (x.powi(2) / 2.0).ln_1p(); + let _ = x.powi(3).ln_1p(); + let _ = (x.powi(3) / 2.0).ln_1p(); let _ = ((std::f32::consts::E - 1.0)).ln_1p(); let _ = x.ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); let _ = (x / 2.0).ln_1p(); // Cases where the lint shouldn't be applied @@ -43,9 +43,9 @@ fn check_ln1p() { let _ = 2.0f64.ln_1p(); let _ = x.ln_1p(); let _ = (x / 2.0).ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = x.ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); let _ = (x / 2.0).ln_1p(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 8be0d9ad56f..01181484e7d 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -25,11 +25,11 @@ fn check_ln1p() { let _ = (1f32 + 2.0).ln(); let _ = (1.0 + x).ln(); let _ = (1.0 + x / 2.0).ln(); - let _ = (1.0 + x.powi(2)).ln(); - let _ = (1.0 + x.powi(2) / 2.0).ln(); + let _ = (1.0 + x.powi(3)).ln(); + let _ = (1.0 + x.powi(3) / 2.0).ln(); let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); let _ = (x + 1.0).ln(); - let _ = (x.powi(2) + 1.0).ln(); + let _ = (x.powi(3) + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln(); // Cases where the lint shouldn't be applied @@ -43,9 +43,9 @@ fn check_ln1p() { let _ = (1f64 + 2.0).ln(); let _ = (1.0 + x).ln(); let _ = (1.0 + x / 2.0).ln(); - let _ = (1.0 + x.powi(2)).ln(); + let _ = (1.0 + x.powi(3)).ln(); let _ = (x + 1.0).ln(); - let _ = (x.powi(2) + 1.0).ln(); + let _ = (x.powi(3) + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index 943fbdb0b83..900dc2b7933 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -77,14 +77,14 @@ LL | let _ = (1.0 + x / 2.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:28:13 | -LL | let _ = (1.0 + x.powi(2)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (1.0 + x.powi(3)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:29:13 | -LL | let _ = (1.0 + x.powi(2) / 2.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()` +LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:30:13 @@ -101,8 +101,8 @@ LL | let _ = (x + 1.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:32:13 | -LL | let _ = (x.powi(2) + 1.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (x.powi(3) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:33:13 @@ -143,8 +143,8 @@ LL | let _ = (1.0 + x / 2.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:46:13 | -LL | let _ = (1.0 + x.powi(2)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (1.0 + x.powi(3)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:47:13 @@ -155,8 +155,8 @@ LL | let _ = (x + 1.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:48:13 | -LL | let _ = (x.powi(2) + 1.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (x.powi(3) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:49:13 diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index 78a9d44829b..b0641a100cd 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -11,7 +11,7 @@ fn main() { let _ = (-3.1f32).exp(); let _ = x.sqrt(); let _ = x.cbrt(); - let _ = x.powi(2); + let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(16_777_215); let _ = x.powi(-16_777_215); @@ -30,7 +30,7 @@ fn main() { let _ = (-3.1f64).exp(); let _ = x.sqrt(); let _ = x.cbrt(); - let _ = x.powi(2); + let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(-2_147_483_648); let _ = x.powi(2_147_483_647); diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index dbc1cac5cb4..a0a2c973900 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -11,7 +11,7 @@ fn main() { let _ = std::f32::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); - let _ = x.powf(2.0); + let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(16_777_215.0); let _ = x.powf(-16_777_215.0); @@ -30,7 +30,7 @@ fn main() { let _ = std::f64::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); - let _ = x.powf(2.0); + let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(-2_147_483_648.0); let _ = x.powf(2_147_483_647.0); diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index ad5163f0079..2422eb911e9 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -53,8 +53,8 @@ LL | let _ = x.powf(1.0 / 3.0); error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:14:13 | -LL | let _ = x.powf(2.0); - | ^^^^^^^^^^^ help: consider using: `x.powi(2)` +LL | let _ = x.powf(3.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:15:13 @@ -125,8 +125,8 @@ LL | let _ = x.powf(1.0 / 3.0); error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:33:13 | -LL | let _ = x.powf(2.0); - | ^^^^^^^^^^^ help: consider using: `x.powi(2)` +LL | let _ = x.powf(3.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:34:13 diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed new file mode 100644 index 00000000000..0ce6f72535d --- /dev/null +++ b/tests/ui/floating_point_powi.fixed @@ -0,0 +1,12 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let one = 1; + let x = 3f32; + let _ = x * x; + let _ = x * x; + // Cases where the lint shouldn't be applied + let _ = x.powi(3); + let _ = x.powi(one + 1); +} diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs new file mode 100644 index 00000000000..c87e836bedd --- /dev/null +++ b/tests/ui/floating_point_powi.rs @@ -0,0 +1,12 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let one = 1; + let x = 3f32; + let _ = x.powi(2); + let _ = x.powi(1 + 1); + // Cases where the lint shouldn't be applied + let _ = x.powi(3); + let _ = x.powi(one + 1); +} diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr new file mode 100644 index 00000000000..ae7bbaa4473 --- /dev/null +++ b/tests/ui/floating_point_powi.stderr @@ -0,0 +1,16 @@ +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:7:13 + | +LL | let _ = x.powi(2); + | ^^^^^^^^^ help: consider using: `x * x` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:8:13 + | +LL | let _ = x.powi(1 + 1); + | ^^^^^^^^^^^^^ help: consider using: `x * x` + +error: aborting due to 2 previous errors + From f62798454c8a7f9f2c3e87e0a913b3bd79b6d2ed Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 25 May 2020 13:54:39 -0300 Subject: [PATCH 0262/1110] Lint (x * x + y * y).sqrt() => x.hypot(y) --- clippy_lints/src/floating_point_arithmetic.rs | 75 ++++++++++++++++++- tests/ui/floating_point_hypot.fixed | 13 ++++ tests/ui/floating_point_hypot.rs | 13 ++++ tests/ui/floating_point_hypot.stderr | 30 ++++++++ tests/ui/floating_point_mul_add.fixed | 5 ++ tests/ui/floating_point_mul_add.rs | 5 ++ tests/ui/floating_point_mul_add.stderr | 8 +- tests/ui/floating_point_powi.fixed | 7 +- tests/ui/floating_point_powi.rs | 7 +- tests/ui/floating_point_powi.stderr | 20 ++++- 10 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 tests/ui/floating_point_hypot.fixed create mode 100644 tests/ui/floating_point_hypot.rs create mode 100644 tests/ui/floating_point_hypot.stderr diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index e3ee4296119..3b2e46a9a85 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,10 +2,10 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -296,6 +296,17 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + // TODO: need more specific check. this is too wide. remember also to include tests + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(grandparent) = get_parent_expr(cx, parent) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } + } + } + } + let (lint, help, suggestion) = match value { Int(2) => ( IMPRECISE_FLOPS, @@ -317,6 +328,57 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } +fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref add_lhs, + ref add_rhs, + ) = args[0].kind + { + // check if expression of the form x * x + y * y + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; + if are_exprs_equal(cx, lmul_lhs, lmul_rhs); + if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + then { + return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); + } + } + + // check if expression of the form x.powi(2) + y.powi(2) + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind; + if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; + if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]); + if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]); + if Int(2) == lvalue && Int(2) == rvalue; + then { + return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); + } + } + } + + None +} + +fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + if let Some(message) = detect_hypot(cx, args) { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "hypotenuse can be computed more accurately", + "consider using", + message, + Applicability::MachineApplicable, + ); + } +} + // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -368,6 +430,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind { + if let Some(parent) = get_parent_expr(cx, expr) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } + } + } + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { (inner_lhs, inner_rhs, rhs) } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { @@ -514,6 +584,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { "log" => check_log_base(cx, expr, args), "powf" => check_powf(cx, expr, args), "powi" => check_powi(cx, expr, args), + "sqrt" => check_hypot(cx, expr, args), _ => {}, } } diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed new file mode 100644 index 00000000000..f90695bc3fe --- /dev/null +++ b/tests/ui/floating_point_hypot.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let y = 4f32; + let _ = x.hypot(y); + let _ = (x + 1f32).hypot(y); + let _ = x.hypot(y); + // Cases where the lint shouldn't be applied + let _ = x.mul_add(x, y * y).sqrt(); + let _ = x.mul_add(4f32, y * y).sqrt(); +} diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs new file mode 100644 index 00000000000..e7b048e262f --- /dev/null +++ b/tests/ui/floating_point_hypot.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let y = 4f32; + let _ = (x * x + y * y).sqrt(); + let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); + let _ = (x.powi(2) + y.powi(2)).sqrt(); + // Cases where the lint shouldn't be applied + let _ = x.mul_add(x, y * y).sqrt(); + let _ = (x * 4f32 + y * y).sqrt(); +} diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr new file mode 100644 index 00000000000..fe1dfc7a451 --- /dev/null +++ b/tests/ui/floating_point_hypot.stderr @@ -0,0 +1,30 @@ +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:7:13 + | +LL | let _ = (x * x + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:8:13 + | +LL | let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:9:13 + | +LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_hypot.rs:12:13 + | +LL | let _ = (x * 4f32 + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(4f32, y * y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index e343c37740d..911700bab00 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -18,4 +18,9 @@ fn main() { let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); + + let _ = a.mul_add(a, b).sqrt(); + + // Cases where the lint shouldn't be applied + let _ = (a * a + b * b).sqrt(); } diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 810f929c856..d202385fc8a 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -18,4 +18,9 @@ fn main() { let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; + + let _ = (a * a + b).sqrt(); + + // Cases where the lint shouldn't be applied + let _ = (a * a + b * b).sqrt(); } diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 2dfbf562d15..ac8d0c0cae0 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: aborting due to 9 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:22:13 + | +LL | let _ = (a * a + b).sqrt(); + | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 0ce6f72535d..98766e68aaf 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,12 +1,17 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let one = 1; let x = 3f32; let _ = x * x; let _ = x * x; + + let y = 4f32; + let _ = (x * x + y).sqrt(); + let _ = (x + y * y).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); + let _ = x.hypot(y); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index c87e836bedd..3c4b636a3d8 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,12 +1,17 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let one = 1; let x = 3f32; let _ = x.powi(2); let _ = x.powi(1 + 1); + + let y = 4f32; + let _ = (x.powi(2) + y).sqrt(); + let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); + let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index ae7bbaa4473..f370e24bf05 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -12,5 +12,23 @@ error: square can be computed more accurately LL | let _ = x.powi(1 + 1); | ^^^^^^^^^^^^^ help: consider using: `x * x` -error: aborting due to 2 previous errors +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:11:14 + | +LL | let _ = (x.powi(2) + y).sqrt(); + | ^^^^^^^^^ help: consider using: `x * x` + +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:12:18 + | +LL | let _ = (x + y.powi(2)).sqrt(); + | ^^^^^^^^^ help: consider using: `y * y` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_powi.rs:16:13 + | +LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + +error: aborting due to 5 previous errors From 2e8a1be444afc6a9b5137d3e7e4fdcfcb1f89e0d Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 Jul 2020 22:10:59 +0200 Subject: [PATCH 0263/1110] new lint: match_like_matches_macro --- CHANGELOG.md | 1 + clippy_lints/src/matches.rs | 395 +++++++++++++++++- .../src/redundant_pattern_matching.rs | 260 ------------ src/lintlist/mod.rs | 9 +- tests/ui/find_map.rs | 1 + tests/ui/find_map.stderr | 2 +- tests/ui/match_expr_like_matches_macro.fixed | 32 ++ tests/ui/match_expr_like_matches_macro.rs | 41 ++ tests/ui/match_expr_like_matches_macro.stderr | 42 ++ tests/ui/neg_cmp_op_on_partial_ord.rs | 2 +- tests/ui/question_mark.fixed | 5 +- tests/ui/question_mark.rs | 5 +- tests/ui/question_mark.stderr | 20 +- tests/ui/redundant_pattern_matching.fixed | 8 +- tests/ui/redundant_pattern_matching.rs | 8 +- tests/ui/redundant_pattern_matching.stderr | 56 +-- ...undant_pattern_matching_const_result.fixed | 2 +- ...redundant_pattern_matching_const_result.rs | 2 +- 18 files changed, 563 insertions(+), 328 deletions(-) delete mode 100644 clippy_lints/src/redundant_pattern_matching.rs create mode 100644 tests/ui/match_expr_like_matches_macro.fixed create mode 100644 tests/ui/match_expr_like_matches_macro.rs create mode 100644 tests/ui/match_expr_like_matches_macro.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..6261ca4879a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1513,6 +1513,7 @@ Released 2018-09-13 [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b754a45aa40..34aa2981535 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -13,14 +13,14 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - QPath, RangeEnd, + Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat, + PatKind, QPath, RangeEnd, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; +use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; use std::collections::Bound; @@ -409,6 +409,67 @@ declare_clippy_lint! { "a match on a struct that binds all fields but still uses the wildcard pattern" } +declare_clippy_lint! { + /// **What it does:** Lint for redundant pattern matching over `Result` or + /// `Option` + /// + /// **Why is this bad?** It's more concise and clear to just use the proper + /// utility function + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// if let Ok(_) = Ok::(42) {} + /// if let Err(_) = Err::(42) {} + /// if let None = None::<()> {} + /// if let Some(_) = Some(42) {} + /// match Ok::(42) { + /// Ok(_) => true, + /// Err(_) => false, + /// }; + /// ``` + /// + /// The more idiomatic use would be: + /// + /// ```rust + /// if Ok::(42).is_ok() {} + /// if Err::(42).is_err() {} + /// if None::<()>.is_none() {} + /// if Some(42).is_some() {} + /// Ok::(42).is_ok(); + /// ``` + pub REDUNDANT_PATTERN_MATCHING, + style, + "use the proper utility function avoiding an `if let`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `match` expressions producing a `bool` that could be written using `matches!` + /// + /// **Why is this bad?** Readability and needless complexity. + /// + /// **Known problems:** This can turn an intentionally exhaustive match into a non-exhaustive one. + /// + /// **Example:** + /// ```rust + /// let x = Some(5); + /// + /// // Bad + /// let a = match x { + /// Some(0) => true, + /// _ => false, + /// }; + /// + /// // Good + /// let a = matches!(x, Some(5)); + /// ``` + pub MATCH_LIKE_MATCHES_MACRO, + style, + "a match that could be written with the matches! macro" +} + #[derive(Default)] pub struct Matches { infallible_destructuring_match_linted: bool, @@ -427,7 +488,9 @@ impl_lint_pass!(Matches => [ WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, INFALLIBLE_DESTRUCTURING_MATCH, - REST_PAT_IN_FULLY_BOUND_STRUCTS + REST_PAT_IN_FULLY_BOUND_STRUCTS, + REDUNDANT_PATTERN_MATCHING, + MATCH_LIKE_MATCHES_MACRO ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -435,6 +498,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if in_external_macro(cx.sess(), expr.span) { return; } + + if !redundant_pattern_match::check(cx, expr) { + check_match_like_matches(cx, expr); + } + if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr); @@ -802,13 +870,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // Some simple checks for exhaustive patterns. // There is a room for improvements to detect more cases, // but it can be more expensive to do so. - let is_pattern_exhaustive = |pat: &&Pat<'_>| { - if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { - true - } else { - false - } - }; + let is_pattern_exhaustive = + |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); if patterns.iter().all(is_pattern_exhaustive) { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } @@ -989,6 +1052,78 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { } } +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), + MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), + _ => return, + } + } +} + +/// Lint a `match` or desugared `if let` for replacement by `matches!` +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) { + if_chain! { + if arms.len() == 2; + if cx.tables().expr_ty(expr).is_bool(); + if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); + if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); + if first != second; + then { + let mut applicability = Applicability::MachineApplicable; + + let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard { + format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability)) + } else { + format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability)) + }; + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }), + "try this", + format!( + "{}matches!({}, {})", + if first { "" } else { "!" }, + snippet_with_applicability(cx, ex.span, "..", &mut applicability), + pat_and_guard, + ), + applicability, + ) + } + } +} + +/// Extract a `bool` or `{ bool }` +fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { + match ex { + ExprKind::Lit(Spanned { + node: LitKind::Bool(b), .. + }) => Some(*b), + ExprKind::Block( + rustc_hir::Block { + stmts: &[], + expr: Some(exp), + .. + }, + _, + ) if desugared => { + if let ExprKind::Lit(Spanned { + node: LitKind::Bool(b), .. + }) = exp.kind + { + Some(b) + } else { + None + } + }, + _ => None, + } +} + fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; @@ -1179,10 +1314,7 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool { // Checks if arm has the form `None => None` fn is_none_arm(arm: &Arm<'_>) -> bool { - match arm.pat.kind { - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true, - _ => false, - } + matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE)) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) @@ -1293,6 +1425,239 @@ where None } +mod redundant_pattern_match { + use super::REDUNDANT_PATTERN_MATCHING; + use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; + use if_chain::if_chain; + use rustc_ast::ast::LitKind; + use rustc_errors::Applicability; + use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; + use rustc_lint::LateContext; + use rustc_middle::ty; + use rustc_mir::const_eval::is_const_fn; + use rustc_span::source_map::Symbol; + + pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), + MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), + _ => false, + } + } else { + false + } + } + + fn find_sugg_for_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + keyword: &'static str, + ) -> bool { + fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { + if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { + return Some("is_ok()"); + } + if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { + return Some("is_err()"); + } + if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { + return Some("is_some()"); + } + if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { + return Some("is_none()"); + } + None + } + + let hir_id = expr.hir_id; + let good_method = match arms[0].pat.kind { + PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + if let PatKind::Wild = patterns[0].kind { + find_suggestion(cx, hir_id, path) + } else { + None + } + }, + PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), + _ => None, + }; + let good_method = match good_method { + Some(method) => method, + None => return false, + }; + + // check that `while_let_on_iterator` lint does not trigger + if_chain! { + if keyword == "while"; + if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; + if method_path.ident.name == sym!(next); + if match_trait_method(cx, op, &paths::ITERATOR); + then { + return false; + } + } + + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + arms[0].pat.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let expr_span = expr.span; + + // while let ... = ... { ... } + // ^^^ + let op_span = op.span.source_callsite(); + + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^ + let span = expr_span.until(op_span.shrink_to_hi()); + diag.span_suggestion( + span, + "try this", + format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), + Applicability::MachineApplicable, // snippet + ); + }, + ); + true + } + + fn find_sugg_for_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + ) -> bool { + if arms.len() == 2 { + let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + + let hir_id = expr.hir_id; + let found_good_method = match node_pair { + ( + PatKind::TupleStruct(ref path_left, ref patterns_left, _), + PatKind::TupleStruct(ref path_right, ref patterns_right, _), + ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::RESULT_OK, + &paths::RESULT_ERR, + "is_ok()", + "is_err()", + || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), + || can_suggest(cx, hir_id, sym!(result_type), "is_err"), + ) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) + if patterns.len() == 1 => + { + if let PatKind::Wild = patterns[0].kind { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::OPTION_SOME, + &paths::OPTION_NONE, + "is_some()", + "is_none()", + || can_suggest(cx, hir_id, sym!(option_type), "is_some"), + || can_suggest(cx, hir_id, sym!(option_type), "is_none"), + ) + } else { + None + } + }, + _ => None, + }; + + if let Some(good_method) = found_good_method { + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + expr.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + let span = expr.span.to(op.span); + diag.span_suggestion( + span, + "try this", + format!("{}.{}", snippet(cx, op.span, "_"), good_method), + Applicability::MaybeIncorrect, // snippet + ); + }, + ); + return true; + } + } + false + } + + #[allow(clippy::too_many_arguments)] + fn find_good_method_for_match<'a>( + arms: &[Arm<'_>], + path_left: &QPath<'_>, + path_right: &QPath<'_>, + expected_left: &[&str], + expected_right: &[&str], + should_be_left: &'a str, + should_be_right: &'a str, + can_suggest_left: impl Fn() -> bool, + can_suggest_right: impl Fn() -> bool, + ) -> Option<&'a str> { + let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + (&(*arms[0].body).kind, &(*arms[1].body).kind) + } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + (&(*arms[1].body).kind, &(*arms[0].body).kind) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), + _ => None, + }, + _ => None, + } + } + + fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { + if !in_constant(cx, hir_id) { + return true; + } + + // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. + cx.tcx + .get_diagnostic_item(diag_item) + .and_then(|def_id| { + cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .find_map(|item| match item.kind { + ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), + _ => None, + }) + }) + }) + .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) + } +} + #[test] fn test_overlapping() { use rustc_span::source_map::DUMMY_SP; diff --git a/clippy_lints/src/redundant_pattern_matching.rs b/clippy_lints/src/redundant_pattern_matching.rs deleted file mode 100644 index d8d16efb978..00000000000 --- a/clippy_lints/src/redundant_pattern_matching.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_mir::const_eval::is_const_fn; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Symbol; - -declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result` or - /// `Option` - /// - /// **Why is this bad?** It's more concise and clear to just use the proper - /// utility function - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// if let Ok(_) = Ok::(42) {} - /// if let Err(_) = Err::(42) {} - /// if let None = None::<()> {} - /// if let Some(_) = Some(42) {} - /// match Ok::(42) { - /// Ok(_) => true, - /// Err(_) => false, - /// }; - /// ``` - /// - /// The more idiomatic use would be: - /// - /// ```rust - /// if Ok::(42).is_ok() {} - /// if Err::(42).is_err() {} - /// if None::<()>.is_none() {} - /// if Some(42).is_some() {} - /// Ok::(42).is_ok(); - /// ``` - pub REDUNDANT_PATTERN_MATCHING, - style, - "use the proper utility function avoiding an `if let`" -} - -declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]); - -impl<'tcx> LateLintPass<'tcx> for RedundantPatternMatching { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { - match match_source { - MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), - MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), - MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), - _ => return, - } - } - } -} - -fn find_sugg_for_if_let<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], - keyword: &'static str, -) { - fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { - if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { - return Some("is_ok()"); - } - if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { - return Some("is_err()"); - } - if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { - return Some("is_some()"); - } - if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { - return Some("is_none()"); - } - None - } - - let hir_id = expr.hir_id; - let good_method = match arms[0].pat.kind { - PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { - find_suggestion(cx, hir_id, path) - } else { - None - } - }, - PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), - _ => None, - }; - let good_method = match good_method { - Some(method) => method, - None => return, - }; - - // check that `while_let_on_iterator` lint does not trigger - if_chain! { - if keyword == "while"; - if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; - if method_path.ident.name == sym!(next); - if match_trait_method(cx, op, &paths::ITERATOR); - then { - return; - } - } - - span_lint_and_then( - cx, - REDUNDANT_PATTERN_MATCHING, - arms[0].pat.span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |diag| { - // while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - let expr_span = expr.span; - - // while let ... = ... { ... } - // ^^^ - let op_span = op.span.source_callsite(); - - // while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^ - let span = expr_span.until(op_span.shrink_to_hi()); - diag.span_suggestion( - span, - "try this", - format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), - Applicability::MachineApplicable, // snippet - ); - }, - ); -} - -fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - - let hir_id = expr.hir_id; - let found_good_method = match node_pair { - ( - PatKind::TupleStruct(ref path_left, ref patterns_left, _), - PatKind::TupleStruct(ref path_right, ref patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { - find_good_method_for_match( - arms, - path_left, - path_right, - &paths::RESULT_OK, - &paths::RESULT_ERR, - "is_ok()", - "is_err()", - || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), - || can_suggest(cx, hir_id, sym!(result_type), "is_err"), - ) - } else { - None - } - }, - (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) - if patterns.len() == 1 => - { - if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( - arms, - path_left, - path_right, - &paths::OPTION_SOME, - &paths::OPTION_NONE, - "is_some()", - "is_none()", - || can_suggest(cx, hir_id, sym!(option_type), "is_some"), - || can_suggest(cx, hir_id, sym!(option_type), "is_none"), - ) - } else { - None - } - }, - _ => None, - }; - - if let Some(good_method) = found_good_method { - span_lint_and_then( - cx, - REDUNDANT_PATTERN_MATCHING, - expr.span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |diag| { - let span = expr.span.to(op.span); - diag.span_suggestion( - span, - "try this", - format!("{}.{}", snippet(cx, op.span, "_"), good_method), - Applicability::MaybeIncorrect, // snippet - ); - }, - ); - } - } -} - -#[allow(clippy::too_many_arguments)] -fn find_good_method_for_match<'a>( - arms: &[Arm<'_>], - path_left: &QPath<'_>, - path_right: &QPath<'_>, - expected_left: &[&str], - expected_right: &[&str], - should_be_left: &'a str, - should_be_right: &'a str, - can_suggest_left: impl Fn() -> bool, - can_suggest_right: impl Fn() -> bool, -) -> Option<&'a str> { - let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { - (&(*arms[0].body).kind, &(*arms[1].body).kind) - } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { - (&(*arms[1].body).kind, &(*arms[0].body).kind) - } else { - return None; - }; - - match body_node_pair { - (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { - (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), - (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), - _ => None, - }, - _ => None, - } -} - -fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { - if !in_constant(cx, hir_id) { - return true; - } - - // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. - cx.tcx - .get_diagnostic_item(diag_item) - .and_then(|def_id| { - cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { - cx.tcx - .associated_items(*imp) - .in_definition_order() - .find_map(|item| match item.kind { - ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), - _ => None, - }) - }) - }) - .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..888b4755484 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1179,6 +1179,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, + Lint { + name: "match_like_matches_macro", + group: "style", + desc: "a match that could be written with the matches! macro", + deprecation: None, + module: "matches", + }, Lint { name: "match_on_vec_items", group: "pedantic", @@ -1856,7 +1863,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "use the proper utility function avoiding an `if let`", deprecation: None, - module: "redundant_pattern_matching", + module: "matches", }, Lint { name: "redundant_pub_crate", diff --git a/tests/ui/find_map.rs b/tests/ui/find_map.rs index c28cca144ca..88d3b0e7490 100644 --- a/tests/ui/find_map.rs +++ b/tests/ui/find_map.rs @@ -19,6 +19,7 @@ fn main() { let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); + #[allow(clippy::match_like_matches_macro)] let _: Option = desserts_of_the_week .iter() .find(|dessert| match *dessert { diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr index 92f40fe6f1f..f279850fef8 100644 --- a/tests/ui/find_map.stderr +++ b/tests/ui/find_map.stderr @@ -8,7 +8,7 @@ LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s = help: this is more succinctly expressed by calling `.find_map(..)` instead error: called `find(p).map(q)` on an `Iterator` - --> $DIR/find_map.rs:22:29 + --> $DIR/find_map.rs:23:29 | LL | let _: Option = desserts_of_the_week | _____________________________^ diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed new file mode 100644 index 00000000000..2d1ac8836d6 --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -0,0 +1,32 @@ +// run-rustfix + +#![warn(clippy::match_like_matches_macro)] + +fn main() { + let x = Some(5); + + // Lint + let _y = matches!(x, Some(0)); + + // Turn into is_none + let _z = x.is_none(); + + // Lint + let _z = !matches!(x, Some(r) if r == 0); + + // Lint + let _zz = matches!(x, Some(5)); + + // No lint + let _a = match x { + Some(_) => false, + None => false, + }; + + // No lint + let _a = match x { + Some(0) => false, + Some(_) => true, + None => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs new file mode 100644 index 00000000000..376abf9244e --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::match_like_matches_macro)] + +fn main() { + let x = Some(5); + + // Lint + let _y = match x { + Some(0) => true, + _ => false, + }; + + // Turn into is_none + let _z = match x { + Some(_) => false, + None => true, + }; + + // Lint + let _z = match x { + Some(r) if r == 0 => false, + _ => true, + }; + + // Lint + let _zz = if let Some(5) = x { true } else { false }; + + // No lint + let _a = match x { + Some(_) => false, + None => false, + }; + + // No lint + let _a = match x { + Some(0) => false, + Some(_) => true, + None => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr new file mode 100644 index 00000000000..0b32af039a8 --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -0,0 +1,42 @@ +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:9:14 + | +LL | let _y = match x { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(x, Some(0))` + | + = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_expr_like_matches_macro.rs:15:14 + | +LL | let _z = match x { + | ______________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `x.is_none()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:21:14 + | +LL | let _z = match x { + | ______________^ +LL | | Some(r) if r == 0 => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` + +error: if let .. else expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:27:15 + | +LL | let _zz = if let Some(5) = x { true } else { false }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/neg_cmp_op_on_partial_ord.rs b/tests/ui/neg_cmp_op_on_partial_ord.rs index ca70e3b7148..0cee0a28fc7 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.rs +++ b/tests/ui/neg_cmp_op_on_partial_ord.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; -#[allow(clippy::unnested_or_patterns)] +#[allow(clippy::unnested_or_patterns, clippy::match_like_matches_macro)] #[warn(clippy::neg_cmp_op_on_partial_ord)] fn main() { let a_value = 1.0; diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 11dff94a288..bd13cf1bdfa 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -23,10 +23,7 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - match *self { - SeemsOption::None => true, - SeemsOption::Some(_) => false, - } + matches!(*self, SeemsOption::None) } } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 1d0ee82b4f7..94479e68555 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -25,10 +25,7 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - match *self { - SeemsOption::None => true, - SeemsOption::Some(_) => false, - } + matches!(*self, SeemsOption::None) } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 502615fb175..be323035d6c 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:50:9 + --> $DIR/question_mark.rs:47:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:54:9 + --> $DIR/question_mark.rs:51:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:58:17 + --> $DIR/question_mark.rs:55:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:64:17 + --> $DIR/question_mark.rs:61:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:81:9 + --> $DIR/question_mark.rs:78:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:89:9 + --> $DIR/question_mark.rs:86:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:97:9 + --> $DIR/question_mark.rs:94:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:104:26 + --> $DIR/question_mark.rs:101:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:114:17 + --> $DIR/question_mark.rs:111:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:129:5 + --> $DIR/question_mark.rs:126:5 | LL | / if f().is_none() { LL | | return None; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 8b4e2d21331..ce8582d2b22 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -2,7 +2,13 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] fn main() { if Ok::(42).is_ok() {} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index b0904e41b6f..a3a9aa40e3b 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -2,7 +2,13 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] fn main() { if let Ok(_) = Ok::(42) {} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 51a6f4350d3..25d1476062e 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:8:12 + --> $DIR/redundant_pattern_matching.rs:14:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` @@ -7,67 +7,67 @@ LL | if let Ok(_) = Ok::(42) {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:10:12 + --> $DIR/redundant_pattern_matching.rs:16:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:12:12 + --> $DIR/redundant_pattern_matching.rs:18:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:20:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:22:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:22:15 + --> $DIR/redundant_pattern_matching.rs:28:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:24:15 + --> $DIR/redundant_pattern_matching.rs:30:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:26:15 + --> $DIR/redundant_pattern_matching.rs:32:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:34:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:36:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:33:15 + --> $DIR/redundant_pattern_matching.rs:39:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 + --> $DIR/redundant_pattern_matching.rs:55:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:54:5 + --> $DIR/redundant_pattern_matching.rs:60:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +85,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:59:5 + --> $DIR/redundant_pattern_matching.rs:65:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:64:5 + --> $DIR/redundant_pattern_matching.rs:70:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +103,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:69:5 + --> $DIR/redundant_pattern_matching.rs:75:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:74:5 + --> $DIR/redundant_pattern_matching.rs:80:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:79:13 + --> $DIR/redundant_pattern_matching.rs:85:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,61 +131,61 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:84:20 + --> $DIR/redundant_pattern_matching.rs:90:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:87:20 + --> $DIR/redundant_pattern_matching.rs:93:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:99:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:95:19 + --> $DIR/redundant_pattern_matching.rs:101:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:97:19 + --> $DIR/redundant_pattern_matching.rs:103:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:99:19 + --> $DIR/redundant_pattern_matching.rs:105:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:132:19 + --> $DIR/redundant_pattern_matching.rs:138:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:133:16 + --> $DIR/redundant_pattern_matching.rs:139:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:12 + --> $DIR/redundant_pattern_matching.rs:145:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:140:15 + --> $DIR/redundant_pattern_matching.rs:146:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` diff --git a/tests/ui/redundant_pattern_matching_const_result.fixed b/tests/ui/redundant_pattern_matching_const_result.fixed index 8a81e92f04a..de3fe00d5fa 100644 --- a/tests/ui/redundant_pattern_matching_const_result.fixed +++ b/tests/ui/redundant_pattern_matching_const_result.fixed @@ -2,7 +2,7 @@ #![feature(const_result)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused)] +#![allow(clippy::match_like_matches_macro, unused)] // Test that results are linted with the feature enabled. diff --git a/tests/ui/redundant_pattern_matching_const_result.rs b/tests/ui/redundant_pattern_matching_const_result.rs index 1cd515441d1..b77969d53d9 100644 --- a/tests/ui/redundant_pattern_matching_const_result.rs +++ b/tests/ui/redundant_pattern_matching_const_result.rs @@ -2,7 +2,7 @@ #![feature(const_result)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused)] +#![allow(clippy::match_like_matches_macro, unused)] // Test that results are linted with the feature enabled. From 0c8afa39ce8b2e7f0f9be7fc33fe3993d9bc3c53 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 1 Jun 2020 18:32:52 -0300 Subject: [PATCH 0264/1110] Lint x.log(b) / y.log(b) => x.log(y) --- clippy_lints/src/floating_point_arithmetic.rs | 65 ++++++++++++++++--- tests/ui/floating_point_logbase.fixed | 16 +++++ tests/ui/floating_point_logbase.rs | 16 +++++ tests/ui/floating_point_logbase.stderr | 28 ++++++++ 4 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 tests/ui/floating_point_logbase.fixed create mode 100644 tests/ui/floating_point_logbase.rs create mode 100644 tests/ui/floating_point_logbase.stderr diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3b2e46a9a85..9f241c2c3a2 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -293,13 +293,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } -fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument - if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) { // TODO: need more specific check. this is too wide. remember also to include tests if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { return; } @@ -328,7 +328,7 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } -fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { +fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if let ExprKind::Binary( Spanned { node: BinOpKind::Add, .. @@ -350,11 +350,11 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { // check if expression of the form x.powi(2) + y.powi(2) if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind; - if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs, _) = add_lhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs, _) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]); - if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]); + if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]); + if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]); if Int(2) == lvalue && Int(2) == rvalue; then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); @@ -365,7 +365,7 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { None } -fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { if let Some(message) = detect_hypot(cx, args) { span_lint_and_sugg( cx, @@ -431,7 +431,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { ) = &expr.kind { if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind { if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { return; } @@ -573,6 +573,50 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind; + then { + return method_name_a.as_str() == method_name_b.as_str() && + args_a.len() == args_b.len() && + ( + ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || + method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + ); + } + } + + false +} + +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { + // check if expression of the form x.logN() / y.logN() + if_chain! { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + lhs, + rhs, + ) = &expr.kind; + if are_same_base_logs(cx, lhs, rhs); + if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind; + if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind; + then { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "division of logarithms can be calculated more efficiently and accurately", + "consider using", + format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), + Applicability::MachineApplicable, + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { @@ -592,6 +636,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { check_expm1(cx, expr); check_mul_add(cx, expr); check_custom_abs(cx, expr); + check_log_division(cx, expr); } } } diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed new file mode 100644 index 00000000000..13962a272d4 --- /dev/null +++ b/tests/ui/floating_point_logbase.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let x = 3f32; + let y = 5f32; + let _ = x.log(y); + let _ = x.log(y); + let _ = x.log(y); + let _ = x.log(y); + // Cases where the lint shouldn't be applied + let _ = x.ln() / y.powf(3.2); + let _ = x.powf(3.2) / y.powf(3.2); + let _ = x.powf(3.2) / y.ln(); + let _ = x.log(5f32) / y.log(7f32); +} diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs new file mode 100644 index 00000000000..26bc20d5370 --- /dev/null +++ b/tests/ui/floating_point_logbase.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let x = 3f32; + let y = 5f32; + let _ = x.ln() / y.ln(); + let _ = x.log2() / y.log2(); + let _ = x.log10() / y.log10(); + let _ = x.log(5f32) / y.log(5f32); + // Cases where the lint shouldn't be applied + let _ = x.ln() / y.powf(3.2); + let _ = x.powf(3.2) / y.powf(3.2); + let _ = x.powf(3.2) / y.ln(); + let _ = x.log(5f32) / y.log(7f32); +} diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr new file mode 100644 index 00000000000..fa956b9139e --- /dev/null +++ b/tests/ui/floating_point_logbase.stderr @@ -0,0 +1,28 @@ +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:7:13 + | +LL | let _ = x.ln() / y.ln(); + | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:8:13 + | +LL | let _ = x.log2() / y.log2(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:9:13 + | +LL | let _ = x.log10() / y.log10(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:10:13 + | +LL | let _ = x.log(5f32) / y.log(5f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: aborting due to 4 previous errors + From 1740dda76386aff7205b2a709a32c95e8cbc0d57 Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 Jul 2020 22:11:19 +0200 Subject: [PATCH 0265/1110] fix match_like_matches_macro in clippy --- clippy_lints/src/assign_ops.rs | 1 + clippy_lints/src/comparison_chain.rs | 5 +--- clippy_lints/src/eq_op.rs | 30 +++++++++++------------ clippy_lints/src/escape.rs | 5 +--- clippy_lints/src/eta_reduction.rs | 5 +--- clippy_lints/src/formatting.rs | 12 ++------- clippy_lints/src/functions.rs | 8 +----- clippy_lints/src/lib.rs | 11 +++++---- clippy_lints/src/lifetimes.rs | 16 ++++++------ clippy_lints/src/loops.rs | 10 ++------ clippy_lints/src/methods/mod.rs | 16 ++++-------- clippy_lints/src/misc.rs | 7 +----- clippy_lints/src/misc_early.rs | 18 +++++--------- clippy_lints/src/missing_inline.rs | 9 ++++--- clippy_lints/src/new_without_default.rs | 10 +++++--- clippy_lints/src/non_copy_const.rs | 8 +++--- clippy_lints/src/precedence.rs | 10 ++------ clippy_lints/src/regex.rs | 7 +----- clippy_lints/src/shadow.rs | 5 +--- clippy_lints/src/temporary_assignment.rs | 8 +----- clippy_lints/src/types.rs | 29 ++++++---------------- clippy_lints/src/unnamed_address.rs | 14 ++++------- clippy_lints/src/use_self.rs | 6 ++--- clippy_lints/src/utils/ast_utils.rs | 5 +--- clippy_lints/src/utils/mod.rs | 11 ++------- clippy_lints/src/utils/numeric_literal.rs | 2 +- src/driver.rs | 9 ++----- 27 files changed, 91 insertions(+), 186 deletions(-) diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index bc6e868823f..3d48bf739eb 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -237,6 +237,7 @@ fn is_commutative(op: hir::BinOpKind) -> bool { use rustc_hir::BinOpKind::{ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, }; + #[allow(clippy::match_like_matches_macro)] match op { Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true, Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false, diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 26476af4cb6..25ccabc1c88 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -122,8 +122,5 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } fn kind_is_cmp(kind: BinOpKind) -> bool { - match kind { - BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true, - _ => false, - } + matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index ca921dcfdfe..7839908fe4c 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -214,20 +214,20 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } fn is_valid_operator(op: BinOp) -> bool { - match op.node { + matches!( + op.node, BinOpKind::Sub - | BinOpKind::Div - | BinOpKind::Eq - | BinOpKind::Lt - | BinOpKind::Le - | BinOpKind::Gt - | BinOpKind::Ge - | BinOpKind::Ne - | BinOpKind::And - | BinOpKind::Or - | BinOpKind::BitXor - | BinOpKind::BitAnd - | BinOpKind::BitOr => true, - _ => false, - } + | BinOpKind::Div + | BinOpKind::Eq + | BinOpKind::Lt + | BinOpKind::Le + | BinOpKind::Gt + | BinOpKind::Ge + | BinOpKind::Ne + | BinOpKind::And + | BinOpKind::Or + | BinOpKind::BitXor + | BinOpKind::BitAnd + | BinOpKind::BitOr + ) } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index d40cdfcca9f..32fc01149d8 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -105,10 +105,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { _ => return false, } - match map.find(map.get_parent_node(id)) { - Some(Node::Param(_)) => true, - _ => false, - } + matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_))) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index ceed6a74c4f..fb26b9fc27d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -175,10 +175,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { match (&lhs.kind, &rhs.kind) { (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), - (l, r) => match (l, r) { - (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false, - (_, _) => true, - }, + (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 156246fb8bb..1bd16e6cce5 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -305,18 +305,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { } fn is_block(expr: &Expr) -> bool { - if let ExprKind::Block(..) = expr.kind { - true - } else { - false - } + matches!(expr.kind, ExprKind::Block(..)) } /// Check if the expression is an `if` or `if let` fn is_if(expr: &Expr) -> bool { - if let ExprKind::If(..) = expr.kind { - true - } else { - false - } + matches!(expr.kind, ExprKind::If(..)) } diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 3f030dd8422..63133a4872a 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -645,13 +645,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { use hir::ExprKind::{Field, Index, Path}; match e.kind { - Path(ref qpath) => { - if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) { - false - } else { - true - } - }, + Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)), Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner), _ => false, } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..c4f1af8f4e4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -277,7 +277,6 @@ mod question_mark; mod ranges; mod redundant_clone; mod redundant_field_names; -mod redundant_pattern_matching; mod redundant_pub_crate; mod redundant_static_lifetimes; mod reference; @@ -623,11 +622,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::INFALLIBLE_DESTRUCTURING_MATCH, &matches::MATCH_AS_REF, &matches::MATCH_BOOL, + &matches::MATCH_LIKE_MATCHES_MACRO, &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, &matches::MATCH_SINGLE_BINDING, &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, + &matches::REDUNDANT_PATTERN_MATCHING, &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, &matches::SINGLE_MATCH, &matches::SINGLE_MATCH_ELSE, @@ -757,7 +758,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_field_names::REDUNDANT_FIELD_NAMES, - &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, &reference::DEREF_ADDROF, @@ -956,7 +956,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); store.register_late_pass(|| box if_let_some_result::OkIfLet); - store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); let enum_variant_size_threshold = conf.enum_variant_size_threshold; @@ -1295,9 +1294,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_AS_REF), + LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_SINGLE_BINDING), + LintId::of(&matches::REDUNDANT_PATTERN_MATCHING), LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), @@ -1387,7 +1388,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), @@ -1488,8 +1488,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), + LintId::of(&matches::REDUNDANT_PATTERN_MATCHING), LintId::of(&matches::SINGLE_MATCH), LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), @@ -1526,7 +1528,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::PTR_ARG), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index a79f94855bd..168f9f953e4 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -129,10 +129,10 @@ fn check_fn_inner<'tcx>( } let mut bounds_lts = Vec::new(); - let types = generics.params.iter().filter(|param| match param.kind { - GenericParamKind::Type { .. } => true, - _ => false, - }); + let types = generics + .params + .iter() + .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { for bound in typ.bounds { let mut visitor = RefVisitor::new(cx); @@ -337,10 +337,10 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { if let Some(ref last_path_segment) = last_path_segment(qpath).args { if !last_path_segment.parenthesized - && !last_path_segment.args.iter().any(|arg| match arg { - GenericArg::Lifetime(_) => true, - _ => false, - }) + && !last_path_segment + .args + .iter() + .any(|arg| matches!(arg, GenericArg::Lifetime(_))) { let hir_id = ty.hir_id; match self.cx.qpath_res(qpath, hir_id) { diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index b803d753b6d..396bb659109 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2091,17 +2091,11 @@ fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } fn is_loop(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Loop(..) => true, - _ => false, - } + matches!(expr.kind, ExprKind::Loop(..)) } fn is_conditional(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Match(..) => true, - _ => false, - } + matches!(expr.kind, ExprKind::Match(..)) } fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f1c8894c0ee..4c595029ff7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1844,10 +1844,10 @@ fn lint_expect_fun_call( ty::Ref(ty::ReStatic, ..) ) }), - hir::ExprKind::Path(ref p) => match cx.qpath_res(p, arg.hir_id) { - hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true, - _ => false, - }, + hir::ExprKind::Path(ref p) => matches!( + cx.qpath_res(p, arg.hir_id), + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) + ), _ => false, } } @@ -2028,13 +2028,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp .tables() .expr_adjustments(arg) .iter() - .filter(|adj| { - if let ty::adjustment::Adjust::Deref(_) = adj.kind { - true - } else { - false - } - }) + .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) .count(); let derefs: String = iter::repeat('*').take(deref_count).collect(); snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 3d4225f36a7..400f4b609af 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -694,12 +694,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::MacroKind; if expr.span.from_expansion() { let data = expr.span.ctxt().outer_expn_data(); - - if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind { - true - } else { - false - } + matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _)) } else { false } diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index ad39e59d067..b84a1a3fe24 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -641,28 +641,22 @@ fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { ); } - #[allow(clippy::trivially_copy_pass_by_ref)] - fn is_wild>(pat: &&P) -> bool { - if let PatKind::Wild = pat.kind { - true - } else { - false - } - } - if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { if let Some((left_index, left_pat)) = patterns[..rest_index] .iter() .rev() - .take_while(is_wild) + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) .enumerate() .last() { span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); } - if let Some((right_index, right_pat)) = - patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last() + if let Some((right_index, right_pat)) = patterns[rest_index + 1..] + .iter() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() { span_lint( cx, diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index bf80b62afe6..9c962673537 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -71,10 +71,11 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp fn is_executable(cx: &LateContext<'_>) -> bool { use rustc_session::config::CrateType; - cx.tcx.sess.crate_types().iter().any(|t: &CrateType| match t { - CrateType::Executable => true, - _ => false, - }) + cx.tcx + .sess + .crate_types() + .iter() + .any(|t: &CrateType| matches!(t, CrateType::Executable)) } declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2597f5f6f17..621ebdef2f0 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -80,10 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // can't be implemented for unsafe new return; } - if impl_item.generics.params.iter().any(|gen| match gen.kind { - hir::GenericParamKind::Type { .. } => true, - _ => false, - }) { + if impl_item + .generics + .params + .iter() + .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. })) + { // when the result of `new()` depends on a type parameter we should not require // an // impl of `Default` diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index c11a2ff9ee0..a3521c31a6b 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -238,10 +238,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let ty = if needs_check_adjustment { let adjustments = cx.tables().expr_adjustments(dereferenced_expr); - if let Some(i) = adjustments.iter().position(|adj| match adj.kind { - Adjust::Borrow(_) | Adjust::Deref(_) => true, - _ => false, - }) { + if let Some(i) = adjustments + .iter() + .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_))) + { if i == 0 { cx.tables().expr_ty(dereferenced_expr) } else { diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 7dce23dd223..04be96aa64c 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -148,17 +148,11 @@ fn is_arith_expr(expr: &Expr) -> bool { #[must_use] fn is_bit_op(op: BinOpKind) -> bool { use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr}; - match op { - BitXor | BitAnd | BitOr | Shl | Shr => true, - _ => false, - } + matches!(op, BitXor | BitAnd | BitOr | Shl | Shr) } #[must_use] fn is_arith_op(op: BinOpKind) -> bool { use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub}; - match op { - Add | Sub | Mul | Div | Rem => true, - _ => false, - } + matches!(op, Add | Sub | Mul | Div | Rem) } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index d8c8eff2c85..f204a0ffb2c 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -99,12 +99,7 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { use regex_syntax::hir::Anchor::{EndText, StartText}; use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal}; - let is_literal = |e: &[regex_syntax::hir::Hir]| { - e.iter().all(|e| match *e.kind() { - Literal(_) => true, - _ => false, - }) - }; + let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_))); match *s.kind() { Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"), diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 4cdff63f118..194786c5c41 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -165,10 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { let var_ty = cx.tables().node_type_opt(pat_id); - var_ty.map_or(false, |var_ty| match var_ty.kind { - ty::Adt(..) => false, - _ => true, - }) + var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..))) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 509bbfd27c1..1aeff1baa36 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -25,13 +25,7 @@ declare_clippy_lint! { fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match &expr.kind { ExprKind::Struct(..) | ExprKind::Tup(..) => true, - ExprKind::Path(qpath) => { - if let Res::Def(DefKind::Const, ..) = cx.qpath_res(qpath, expr.hir_id) { - true - } else { - false - } - }, + ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)), _ => false, } } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index d6f31a99bb3..71207caecf5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -775,11 +775,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { .iter() .filter(|arg| { if is_unit(cx.tables().expr_ty(arg)) && !is_unit_literal(arg) { - if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind { - false - } else { - true - } + !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) } else { false } @@ -899,17 +895,11 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { } fn is_unit(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Tuple(slice) if slice.is_empty() => true, - _ => false, - } + matches!(ty.kind, ty::Tuple(slice) if slice.is_empty()) } fn is_unit_literal(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Tup(ref slice) if slice.is_empty() => true, - _ => false, - } + matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) } declare_clippy_lint! { @@ -1154,10 +1144,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { } fn is_isize_or_usize(typ: Ty<'_>) -> bool { - match typ.kind { - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true, - _ => false, - } + matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { @@ -1737,10 +1724,10 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::TraitObject(ref param_bounds, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { - bound.bound_generic_params.iter().any(|gen| match gen.kind { - GenericParamKind::Lifetime { .. } => true, - _ => false, - }) + bound + .bound_generic_params + .iter() + .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) }); if has_lifetime_parameters { // complex trait bounds like A<'a, 'b> diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index b9aa202b328..25d136e564d 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -58,10 +58,10 @@ declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COM impl LateLintPass<'_> for UnnamedAddress { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_comparison(binop: BinOpKind) -> bool { - match binop { - BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true, - _ => false, - } + matches!( + binop, + BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt + ) } fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { @@ -72,11 +72,7 @@ impl LateLintPass<'_> for UnnamedAddress { } fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ty::FnDef(..) = cx.tables().expr_ty(expr).kind { - true - } else { - false - } + matches!(cx.tables().expr_ty(expr).kind, ty::FnDef(..)) } if_chain! { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 39a8c020872..776c6bc57ca 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -169,10 +169,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; let should_check = parameters.as_ref().map_or( true, - |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { - GenericArg::Lifetime(_) => true, - _ => false, - }) + |params| !params.parenthesized + &&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) ); if should_check { diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index e19a79dd8da..58c1103da9f 100755 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -387,10 +387,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { - match (l, r) { - (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true, - _ => false, - } + matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))) } pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a3b79925ff..0b4cba3fc42 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -102,11 +102,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { #[must_use] pub fn in_macro(span: Span) -> bool { if span.from_expansion() { - if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind { - false - } else { - true - } + !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) } else { false } @@ -127,10 +123,7 @@ pub fn is_present_in_source(cx: &T, span: Span) -> bool { /// Checks if given pattern is a wildcard (`_`) pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { - match pat.kind { - PatKind::Wild => true, - _ => false, - } + matches!(pat.kind, PatKind::Wild) } /// Checks if type is struct, enum or union type with the given def path. diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 7a79741b30b..87cb454f654 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -51,7 +51,7 @@ impl<'a> NumericLiteral<'a> { pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) { let (unsuffixed, suffix) = split_suffix(&src, lit_kind); - let float = if let LitKind::Float(..) = lit_kind { true } else { false }; + let float = matches!(lit_kind, LitKind::Float(..)); Some(NumericLiteral::new(unsuffixed, suffix, float)) } else { None diff --git a/src/driver.rs b/src/driver.rs index decd3a79cce..47315fa64cd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -382,13 +382,8 @@ pub fn main() { let should_describe_lints = || { let args: Vec<_> = env::args().collect(); - args.windows(2).any(|args| { - args[1] == "help" - && match args[0].as_str() { - "-W" | "-A" | "-D" | "-F" => true, - _ => false, - } - }) + args.windows(2) + .any(|args| args[1] == "help" && matches!(args[0].as_str(), "-W" | "-A" | "-D" | "-F")) }; if !wrapper_mode && should_describe_lints() { From 076ec872ce122403f3c75f20c773c64b194a5891 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Wed, 10 Jun 2020 13:52:00 -0300 Subject: [PATCH 0266/1110] Lint for to_radians and to_degrees --- clippy_lints/src/floating_point_arithmetic.rs | 64 ++++++++++++++++++- tests/ui/floating_point_rad.fixed | 13 ++++ tests/ui/floating_point_rad.rs | 13 ++++ tests/ui/floating_point_rad.stderr | 16 +++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 tests/ui/floating_point_rad.fixed create mode 100644 tests/ui/floating_point_rad.rs create mode 100644 tests/ui/floating_point_rad.stderr diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 9f241c2c3a2..d88e47f396c 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -350,8 +350,18 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { // check if expression of the form x.powi(2) + y.powi(2) if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs, _) = add_lhs.kind; - if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs, _) = add_rhs.kind; + if let ExprKind::MethodCall( + PathSegment { ident: lmethod_name, .. }, + ref _lspan, + ref largs, + _ + ) = add_lhs.kind; + if let ExprKind::MethodCall( + PathSegment { ident: rmethod_name, .. }, + ref _rspan, + ref rargs, + _ + ) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]); if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]); @@ -617,6 +627,55 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + div_lhs, + div_rhs, + ) = &expr.kind; + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + mul_lhs, + mul_rhs, + ) = &div_lhs.kind; + if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs); + if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs); + then { + if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && + (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "conversion to degrees can be done more accurately", + "consider using", + format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")), + Applicability::MachineApplicable, + ); + } else if + (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && + (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "conversion to radians can be done more accurately", + "consider using", + format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { @@ -637,6 +696,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { check_mul_add(cx, expr); check_custom_abs(cx, expr); check_log_division(cx, expr); + check_radians(cx, expr); } } } diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed new file mode 100644 index 00000000000..64461417a6a --- /dev/null +++ b/tests/ui/floating_point_rad.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x.to_degrees(); + let _ = x.to_radians(); + // Cases where the lint shouldn't be applied + let _ = x * 90f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 90f32; + let _ = x * 180f32 / std::f32::consts::E; + let _ = x * std::f32::consts::E / 180f32; +} diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs new file mode 100644 index 00000000000..9046f184b3e --- /dev/null +++ b/tests/ui/floating_point_rad.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x * 180f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 180f32; + // Cases where the lint shouldn't be applied + let _ = x * 90f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 90f32; + let _ = x * 180f32 / std::f32::consts::E; + let _ = x * std::f32::consts::E / 180f32; +} diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr new file mode 100644 index 00000000000..81e81821513 --- /dev/null +++ b/tests/ui/floating_point_rad.stderr @@ -0,0 +1,16 @@ +error: conversion to degrees can be done more accurately + --> $DIR/floating_point_rad.rs:6:13 + | +LL | let _ = x * 180f32 / std::f32::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:7:13 + | +LL | let _ = x * std::f32::consts::PI / 180f32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()` + +error: aborting due to 2 previous errors + From f5596826fa59035e6c57d8968df0d61f69c2537b Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 15 Jun 2020 13:55:12 -0300 Subject: [PATCH 0267/1110] Better copy for lint message Since x.log(y) is actually implemented as x.ln() / y.ln() --- clippy_lints/src/floating_point_arithmetic.rs | 2 +- tests/ui/floating_point_logbase.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d88e47f396c..b1e258f4b16 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -618,7 +618,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "division of logarithms can be calculated more efficiently and accurately", + "log base can be expressed more clearly", "consider using", format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), Applicability::MachineApplicable, diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr index fa956b9139e..78354c2f62d 100644 --- a/tests/ui/floating_point_logbase.stderr +++ b/tests/ui/floating_point_logbase.stderr @@ -1,4 +1,4 @@ -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:7:13 | LL | let _ = x.ln() / y.ln(); @@ -6,19 +6,19 @@ LL | let _ = x.ln() / y.ln(); | = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:8:13 | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:9:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log(5f32) / y.log(5f32); From db7bc6b3bd0e58c8fbf7507713a4214299f39c36 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 15 Jun 2020 13:59:44 -0300 Subject: [PATCH 0268/1110] Place radian lints under suboptimal_flops --- clippy_lints/src/floating_point_arithmetic.rs | 4 ++-- tests/ui/floating_point_rad.fixed | 2 +- tests/ui/floating_point_rad.rs | 2 +- tests/ui/floating_point_rad.stderr | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index b1e258f4b16..beb0b234408 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { { span_lint_and_sugg( cx, - IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS, expr.span, "conversion to degrees can be done more accurately", "consider using", @@ -664,7 +664,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { { span_lint_and_sugg( cx, - IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS, expr.span, "conversion to radians can be done more accurately", "consider using", diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed index 64461417a6a..92480c5db8b 100644 --- a/tests/ui/floating_point_rad.fixed +++ b/tests/ui/floating_point_rad.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs index 9046f184b3e..062e7c3fdc1 100644 --- a/tests/ui/floating_point_rad.rs +++ b/tests/ui/floating_point_rad.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr index 81e81821513..a6ffdca64ee 100644 --- a/tests/ui/floating_point_rad.stderr +++ b/tests/ui/floating_point_rad.stderr @@ -4,7 +4,7 @@ error: conversion to degrees can be done more accurately LL | let _ = x * 180f32 / std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` | - = note: `-D clippy::imprecise-flops` implied by `-D warnings` + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: conversion to radians can be done more accurately --> $DIR/floating_point_rad.rs:7:13 From 6dc066fdb9103122cd918b1b38e26fa6edbc7e2e Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Wed, 17 Jun 2020 13:43:11 -0300 Subject: [PATCH 0269/1110] Includes TODO comment for hypot lint --- tests/ui/floating_point_hypot.fixed | 5 +++-- tests/ui/floating_point_hypot.rs | 3 ++- tests/ui/floating_point_hypot.stderr | 10 +--------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed index f90695bc3fe..bbe411b3f48 100644 --- a/tests/ui/floating_point_hypot.fixed +++ b/tests/ui/floating_point_hypot.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let x = 3f32; @@ -8,6 +8,7 @@ fn main() { let _ = (x + 1f32).hypot(y); let _ = x.hypot(y); // Cases where the lint shouldn't be applied + // TODO: linting this adds some complexity, but could be done let _ = x.mul_add(x, y * y).sqrt(); - let _ = x.mul_add(4f32, y * y).sqrt(); + let _ = (x * 4f32 + y * y).sqrt(); } diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs index e7b048e262f..586fd170ea1 100644 --- a/tests/ui/floating_point_hypot.rs +++ b/tests/ui/floating_point_hypot.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let x = 3f32; @@ -8,6 +8,7 @@ fn main() { let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); let _ = (x.powi(2) + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied + // TODO: linting this adds some complexity, but could be done let _ = x.mul_add(x, y * y).sqrt(); let _ = (x * 4f32 + y * y).sqrt(); } diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr index fe1dfc7a451..42069d9ee9e 100644 --- a/tests/ui/floating_point_hypot.stderr +++ b/tests/ui/floating_point_hypot.stderr @@ -18,13 +18,5 @@ error: hypotenuse can be computed more accurately LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` -error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_hypot.rs:12:13 - | -LL | let _ = (x * 4f32 + y * y).sqrt(); - | ^^^^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(4f32, y * y)` - | - = note: `-D clippy::suboptimal-flops` implied by `-D warnings` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 6be9491eace540d341f3b8dbf775a10e25f6431a Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 22 Jun 2020 14:16:27 -0300 Subject: [PATCH 0270/1110] Reclassify powi(2) lint under suboptimal_flops --- clippy_lints/src/floating_point_arithmetic.rs | 69 ++++++++++++------- tests/ui/floating_point_powi.fixed | 10 +-- tests/ui/floating_point_powi.rs | 4 +- tests/ui/floating_point_powi.stderr | 38 +++++----- 4 files changed, 75 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index beb0b234408..11bd0ae23aa 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -294,37 +294,56 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { - // Check argument if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) { - // TODO: need more specific check. this is too wide. remember also to include tests - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { - if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { - return; + if value == Int(2) { + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(grandparent) = get_parent_expr(cx, parent) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } } } + + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref lhs, + ref rhs, + ) = parent.kind + { + let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + parent.span, + "square can be computed more efficiently", + "consider using", + format!( + "{}.mul_add({}, {})", + Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &other_addend, ".."), + ), + Applicability::MachineApplicable, + ); + + return; + } } - } - let (lint, help, suggestion) = match value { - Int(2) => ( - IMPRECISE_FLOPS, - "square can be computed more accurately", + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "square can be computed more efficiently", + "consider using", format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), - ), - _ => return, - }; - - span_lint_and_sugg( - cx, - lint, - expr.span, - help, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 98766e68aaf..56762400593 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let one = 1; @@ -8,10 +8,12 @@ fn main() { let _ = x * x; let y = 4f32; - let _ = (x * x + y).sqrt(); - let _ = (x + y * y).sqrt(); + let _ = x.mul_add(x, y); + let _ = y.mul_add(y, x); + let _ = x.mul_add(x, y).sqrt(); + let _ = y.mul_add(y, x).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); - let _ = x.hypot(y); + let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 3c4b636a3d8..1f800e4628d 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let one = 1; @@ -8,6 +8,8 @@ fn main() { let _ = x.powi(1 + 1); let y = 4f32; + let _ = x.powi(2) + y; + let _ = x + y.powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index f370e24bf05..d5a5f1bcca1 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,34 +1,40 @@ -error: square can be computed more accurately +error: square can be computed more efficiently --> $DIR/floating_point_powi.rs:7:13 | LL | let _ = x.powi(2); | ^^^^^^^^^ help: consider using: `x * x` | - = note: `-D clippy::imprecise-flops` implied by `-D warnings` + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: square can be computed more accurately +error: square can be computed more efficiently --> $DIR/floating_point_powi.rs:8:13 | LL | let _ = x.powi(1 + 1); | ^^^^^^^^^^^^^ help: consider using: `x * x` -error: square can be computed more accurately - --> $DIR/floating_point_powi.rs:11:14 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:11:13 + | +LL | let _ = x.powi(2) + y; + | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` + +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:12:13 + | +LL | let _ = x + y.powi(2); + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` + +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:13:13 | LL | let _ = (x.powi(2) + y).sqrt(); - | ^^^^^^^^^ help: consider using: `x * x` + | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` -error: square can be computed more accurately - --> $DIR/floating_point_powi.rs:12:18 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:14:13 | LL | let _ = (x + y.powi(2)).sqrt(); - | ^^^^^^^^^ help: consider using: `y * y` + | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: hypotenuse can be computed more accurately - --> $DIR/floating_point_powi.rs:16:13 - | -LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` - -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors From 3065201eb3a0c4976de7ef5b5b924afde1b18325 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 6 Jul 2020 13:14:52 -0300 Subject: [PATCH 0271/1110] =?UTF-8?q?Includes=20TODO=20for=20constants=20e?= =?UTF-8?q?quivalent=20to=20=CF=80/180?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/floating_point_arithmetic.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 11bd0ae23aa..3087d6a940a 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -665,6 +665,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs); if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs); then { + // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) { From 5307cb5614e7498f069bb634ab293e176e63c67f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 4 Jul 2020 22:50:03 +0900 Subject: [PATCH 0272/1110] Add a lint for `.repeat(1)` fix #3028. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/repeat_once.rs | 126 ++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/repeat_once.fixed | 16 ++++ tests/ui/repeat_once.rs | 16 ++++ tests/ui/repeat_once.stderr | 40 ++++++++++ 7 files changed, 211 insertions(+) create mode 100644 clippy_lints/src/repeat_once.rs create mode 100644 tests/ui/repeat_once.fixed create mode 100644 tests/ui/repeat_once.rs create mode 100644 tests/ui/repeat_once.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..c5bbaac0df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1616,6 +1616,7 @@ Released 2018-09-13 [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..0f362dbf86b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -282,6 +282,7 @@ mod redundant_pub_crate; mod redundant_static_lifetimes; mod reference; mod regex; +mod repeat_once; mod returns; mod serde_api; mod shadow; @@ -764,6 +765,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, + &repeat_once::REPEAT_ONCE, &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, @@ -1071,6 +1073,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1393,6 +1396,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), @@ -1602,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), + LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs new file mode 100644 index 00000000000..af3c948ec82 --- /dev/null +++ b/clippy_lints/src/repeat_once.rs @@ -0,0 +1,126 @@ +use crate::consts::{miri_to_const, Constant}; +use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. + /// - `.to_string()` for `str` + /// - `.clone()` for `String` + /// - `.to_vec()` for `slice` + /// + /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind thi, `clone()` should be used. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// let x = String::from("hello world").repeat(1); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// let x = String::from("hello world").clone(); + /// } + /// ``` + pub REPEAT_ONCE, + complexity, + "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` " +} + +declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); + +impl<'tcx> LateLintPass<'tcx> for RepeatOnce { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if path.ident.name == sym!(repeat); + if is_once(cx, &args[1]) && !in_macro(args[0].span); + then { + let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0])); + if is_str(ty){ + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on str", + "consider using `.to_string()` instead", + format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_slice(ty) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on slice", + "consider using `.to_vec()` instead", + format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on a string literal", + "consider using `.clone()` instead", + format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } + } + } + } +} + +fn is_once<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { + match expr.kind { + ExprKind::Lit(ref lit) => { + if let LitKind::Int(ref lit_content, _) = lit.node { + *lit_content == 1 + } else { + false + } + }, + ExprKind::Path(rustc_hir::QPath::Resolved(None, path)) => { + if let Res::Def(DefKind::Const, def_id) = path.res { + let ty = cx.tcx.type_of(def_id); + let con = cx + .tcx + .const_eval_poly(def_id) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)) + .unwrap(); + let con = miri_to_const(con); + con == Some(Constant::Int(1)) + } else { + false + } + }, + _ => false, + } +} + +fn is_str(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Str => true, + _ => false, + } +} + +fn is_slice(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Slice(..) | ty::Array(..) => true, + _ => false, + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..078924d3f9b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1879,6 +1879,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "reference", }, + Lint { + name: "repeat_once", + group: "complexity", + desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ", + deprecation: None, + module: "repeat_once", + }, Lint { name: "rest_pat_in_fully_bound_structs", group: "restriction", diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed new file mode 100644 index 00000000000..a637c22fbcd --- /dev/null +++ b/tests/ui/repeat_once.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::repeat_once)] +#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +fn main() { + const N: usize = 1; + let s = "str"; + let string = "String".to_string(); + let slice = [1; 5]; + + let a = [1; 5].to_vec(); + let b = slice.to_vec(); + let c = "hello".to_string(); + let d = "hi".to_string(); + let e = s.to_string(); + let f = string.clone(); +} diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs new file mode 100644 index 00000000000..d99ca1b5b55 --- /dev/null +++ b/tests/ui/repeat_once.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::repeat_once)] +#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +fn main() { + const N: usize = 1; + let s = "str"; + let string = "String".to_string(); + let slice = [1; 5]; + + let a = [1; 5].repeat(1); + let b = slice.repeat(1); + let c = "hello".repeat(N); + let d = "hi".repeat(1); + let e = s.repeat(1); + let f = string.repeat(1); +} diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr new file mode 100644 index 00000000000..915eea3bfc6 --- /dev/null +++ b/tests/ui/repeat_once.stderr @@ -0,0 +1,40 @@ +error: calling `repeat(1)` on slice + --> $DIR/repeat_once.rs:10:13 + | +LL | let a = [1; 5].repeat(1); + | ^^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `[1; 5].to_vec()` + | + = note: `-D clippy::repeat-once` implied by `-D warnings` + +error: calling `repeat(1)` on slice + --> $DIR/repeat_once.rs:11:13 + | +LL | let b = slice.repeat(1); + | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:12:13 + | +LL | let c = "hello".repeat(N); + | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:13:13 + | +LL | let d = "hi".repeat(1); + | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:14:13 + | +LL | let e = s.repeat(1); + | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` + +error: calling `repeat(1)` on a string literal + --> $DIR/repeat_once.rs:15:13 + | +LL | let f = string.repeat(1); + | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` + +error: aborting due to 6 previous errors + From 37d75da266443dd4253ceedebd692ba77dd72e03 Mon Sep 17 00:00:00 2001 From: robojumper Date: Wed, 8 Jul 2020 18:04:51 +0200 Subject: [PATCH 0273/1110] make match_like_matches_macro only apply to matches with a wildcard --- clippy_lints/src/assign_ops.rs | 1 - clippy_lints/src/matches.rs | 41 +++++++++---------- tests/ui/match_expr_like_matches_macro.fixed | 14 ++++--- tests/ui/match_expr_like_matches_macro.rs | 17 +++++--- tests/ui/match_expr_like_matches_macro.stderr | 28 +++++++++---- tests/ui/question_mark.fixed | 5 ++- tests/ui/question_mark.rs | 5 ++- tests/ui/question_mark.stderr | 20 ++++----- 8 files changed, 77 insertions(+), 54 deletions(-) diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 3d48bf739eb..bc6e868823f 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -237,7 +237,6 @@ fn is_commutative(op: hir::BinOpKind) -> bool { use rustc_hir::BinOpKind::{ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, }; - #[allow(clippy::match_like_matches_macro)] match op { Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true, Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 34aa2981535..aeabb99a30d 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -446,11 +446,12 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `match` expressions producing a `bool` that could be written using `matches!` + /// **What it does:** Checks for `match` or `if let` expressions producing a + /// `bool` that could be written using `matches!` /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** This can turn an intentionally exhaustive match into a non-exhaustive one. + /// **Known problems:** None /// /// **Example:** /// ```rust @@ -462,8 +463,14 @@ declare_clippy_lint! { /// _ => false, /// }; /// + /// let a = if let Some(0) = x { + /// true + /// } else { + /// false + /// }; + /// /// // Good - /// let a = matches!(x, Some(5)); + /// let a = matches!(x, Some(0)); /// ``` pub MATCH_LIKE_MATCHES_MACRO, style, @@ -499,9 +506,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { return; } - if !redundant_pattern_match::check(cx, expr) { - check_match_like_matches(cx, expr); - } + redundant_pattern_match::check(cx, expr); + check_match_like_matches(cx, expr); if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); @@ -1068,6 +1074,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr if_chain! { if arms.len() == 2; if cx.tables().expr_ty(expr).is_bool(); + if is_wild(&arms[1].pat); if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); if first != second; @@ -1437,16 +1444,14 @@ mod redundant_pattern_match { use rustc_mir::const_eval::is_const_fn; use rustc_span::source_map::Symbol; - pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), - _ => false, + _ => {}, } - } else { - false } } @@ -1456,7 +1461,7 @@ mod redundant_pattern_match { op: &Expr<'_>, arms: &[Arm<'_>], keyword: &'static str, - ) -> bool { + ) { fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { return Some("is_ok()"); @@ -1487,7 +1492,7 @@ mod redundant_pattern_match { }; let good_method = match good_method { Some(method) => method, - None => return false, + None => return, }; // check that `while_let_on_iterator` lint does not trigger @@ -1497,7 +1502,7 @@ mod redundant_pattern_match { if method_path.ident.name == sym!(next); if match_trait_method(cx, op, &paths::ITERATOR); then { - return false; + return; } } @@ -1526,15 +1531,9 @@ mod redundant_pattern_match { ); }, ); - true } - fn find_sugg_for_match<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], - ) -> bool { + fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { if arms.len() == 2 { let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); @@ -1599,10 +1598,8 @@ mod redundant_pattern_match { ); }, ); - return true; } } - false } #[allow(clippy::too_many_arguments)] diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 2d1ac8836d6..f3e19092480 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] +#![allow(unreachable_patterns)] fn main() { let x = Some(5); @@ -8,25 +9,28 @@ fn main() { // Lint let _y = matches!(x, Some(0)); + // Lint + let _w = matches!(x, Some(_)); + // Turn into is_none let _z = x.is_none(); // Lint - let _z = !matches!(x, Some(r) if r == 0); + let _zz = !matches!(x, Some(r) if r == 0); // Lint - let _zz = matches!(x, Some(5)); + let _zzz = matches!(x, Some(5)); // No lint let _a = match x { Some(_) => false, - None => false, + _ => false, }; // No lint - let _a = match x { + let _ab = match x { Some(0) => false, - Some(_) => true, + _ => true, None => false, }; } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 376abf9244e..fbae7c18b92 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] +#![allow(unreachable_patterns)] fn main() { let x = Some(5); @@ -11,6 +12,12 @@ fn main() { _ => false, }; + // Lint + let _w = match x { + Some(_) => true, + _ => false, + }; + // Turn into is_none let _z = match x { Some(_) => false, @@ -18,24 +25,24 @@ fn main() { }; // Lint - let _z = match x { + let _zz = match x { Some(r) if r == 0 => false, _ => true, }; // Lint - let _zz = if let Some(5) = x { true } else { false }; + let _zzz = if let Some(5) = x { true } else { false }; // No lint let _a = match x { Some(_) => false, - None => false, + _ => false, }; // No lint - let _a = match x { + let _ab = match x { Some(0) => false, - Some(_) => true, + _ => true, None => false, }; } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 0b32af039a8..4668f8565a6 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:9:14 + --> $DIR/match_expr_like_matches_macro.rs:10:14 | LL | let _y = match x { | ______________^ @@ -10,8 +10,18 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:16:14 + | +LL | let _w = match x { + | ______________^ +LL | | Some(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(x, Some(_))` + error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:15:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _z = match x { | ______________^ @@ -23,20 +33,20 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:21:14 + --> $DIR/match_expr_like_matches_macro.rs:28:15 | -LL | let _z = match x { - | ______________^ +LL | let _zz = match x { + | _______________^ LL | | Some(r) if r == 0 => false, LL | | _ => true, LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:27:15 + --> $DIR/match_expr_like_matches_macro.rs:34:16 | -LL | let _zz = if let Some(5) = x { true } else { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` +LL | let _zzz = if let Some(5) = x { true } else { false }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index bd13cf1bdfa..11dff94a288 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -23,7 +23,10 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - matches!(*self, SeemsOption::None) + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } } } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 94479e68555..1d0ee82b4f7 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -25,7 +25,10 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - matches!(*self, SeemsOption::None) + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index be323035d6c..502615fb175 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:47:9 + --> $DIR/question_mark.rs:50:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:51:9 + --> $DIR/question_mark.rs:54:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:55:17 + --> $DIR/question_mark.rs:58:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:61:17 + --> $DIR/question_mark.rs:64:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:78:9 + --> $DIR/question_mark.rs:81:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:86:9 + --> $DIR/question_mark.rs:89:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:94:9 + --> $DIR/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:101:26 + --> $DIR/question_mark.rs:104:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:111:17 + --> $DIR/question_mark.rs:114:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:126:5 + --> $DIR/question_mark.rs:129:5 | LL | / if f().is_none() { LL | | return None; From db1c946aaa02e1192d271dbcfe4598d726806108 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 30 Jun 2020 21:48:34 +0200 Subject: [PATCH 0274/1110] unnecessary_sort_by: avoid linting if key borrows --- clippy_lints/src/let_and_return.rs | 21 ++--------- clippy_lints/src/unnecessary_sort_by.rs | 48 +++++++++++++++---------- clippy_lints/src/utils/mod.rs | 15 ++++++++ tests/ui/unnecessary_sort_by.fixed | 46 +++++++++++++++++++++--- tests/ui/unnecessary_sort_by.rs | 46 +++++++++++++++++++++--- 5 files changed, 131 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs index ddc41f89f8d..fa560ffb980 100644 --- a/clippy_lints/src/let_and_return.rs +++ b/clippy_lints/src/let_and_return.rs @@ -1,6 +1,5 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently @@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> { borrows: bool, } -impl BorrowVisitor<'_, '_> { - fn fn_def_id(&self, expr: &Expr<'_>) -> Option { - match &expr.kind { - ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id), - ExprKind::Call( - Expr { - kind: ExprKind::Path(qpath), - .. - }, - .., - ) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(), - _ => None, - } - } -} - impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { type Map = Map<'tcx>; @@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { return; } - if let Some(def_id) = self.fn_def_id(expr) { + if let Some(def_id) = fn_def_id(self.cx, expr) { self.borrows = self .cx .tcx diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index d940776817c..91c1789a2ff 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -5,24 +5,23 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** - /// Detects when people use `Vec::sort_by` and pass in a function + /// Detects uses of `Vec::sort_by` passing in a closure /// which compares the two arguments, either directly or indirectly. /// /// **Why is this bad?** /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if - /// possible) than to use `Vec::sort_by` and and a more complicated + /// possible) than to use `Vec::sort_by` and a more complicated /// closure. /// /// **Known problems:** - /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't - /// imported by a use statement in the current frame, then a `use` - /// statement that imports it will need to be added (which this lint - /// can't do). + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already + /// imported by a use statement, then it will need to be added manually. /// /// **Example:** /// @@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { }; let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; + if_chain! { if let ExprKind::Path(QPath::Resolved(_, Path { segments: [PathSegment { ident: left_name, .. }], .. })) = &left_expr.kind; if left_name == left_ident; then { - Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } - else { - Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } else { + if !key_returns_borrow(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) + } } } - } else { - None } } + + None +} + +fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(def_id) = utils::fn_def_id(cx, expr) { + let output = cx.tcx.fn_sig(def_id).output(); + let ty = output.skip_binder(); + return matches!(ty.kind, ty::Ref(..)) + || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + false } impl LateLintPass<'_> for UnnecessarySortBy { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a3b79925ff..93075b9f0b5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1363,6 +1363,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { ) } +/// Returns the `DefId` of the callee if the given expression is a function or method call. +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } +} + pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { lints.iter().any(|lint| { matches!( diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 779fd57707a..c017d1cf9a4 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -2,11 +2,11 @@ use std::cmp::Reverse; -fn id(x: isize) -> isize { - x -} +fn unnecessary_sort_by() { + fn id(x: isize) -> isize { + x + } -fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort(); @@ -24,3 +24,41 @@ fn main() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); } + +// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +mod issue_5754 { + struct Test(String); + + #[derive(PartialOrd, Ord, PartialEq, Eq)] + struct Wrapper<'a>(&'a str); + + impl Test { + fn name(&self) -> &str { + &self.0 + } + + fn wrapped(&self) -> Wrapper<'_> { + Wrapper(&self.0) + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(b.name())); + args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped())); + args.sort_unstable_by(|a, b| a.name().cmp(b.name())); + args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped())); + // Reverse + args.sort_by(|a, b| b.name().cmp(a.name())); + args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped())); + args.sort_unstable_by(|a, b| b.name().cmp(a.name())); + args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped())); + } +} + +fn main() { + unnecessary_sort_by(); + issue_5754::test(); +} diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 0485a5630af..1929c72b2f2 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -2,11 +2,11 @@ use std::cmp::Reverse; -fn id(x: isize) -> isize { - x -} +fn unnecessary_sort_by() { + fn id(x: isize) -> isize { + x + } -fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort_by(|a, b| a.cmp(b)); @@ -24,3 +24,41 @@ fn main() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); } + +// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +mod issue_5754 { + struct Test(String); + + #[derive(PartialOrd, Ord, PartialEq, Eq)] + struct Wrapper<'a>(&'a str); + + impl Test { + fn name(&self) -> &str { + &self.0 + } + + fn wrapped(&self) -> Wrapper<'_> { + Wrapper(&self.0) + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(b.name())); + args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped())); + args.sort_unstable_by(|a, b| a.name().cmp(b.name())); + args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped())); + // Reverse + args.sort_by(|a, b| b.name().cmp(a.name())); + args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped())); + args.sort_unstable_by(|a, b| b.name().cmp(a.name())); + args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped())); + } +} + +fn main() { + unnecessary_sort_by(); + issue_5754::test(); +} From dac19e3afccc63ff976bcf0a5ee385bdd0e075d5 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 7 Jul 2020 00:35:51 +0200 Subject: [PATCH 0275/1110] single_match_else - single expr/stmt else block corner case --- clippy_lints/src/matches.rs | 20 +++++++----- tests/ui/single_match_else.rs | 51 +++++++++++++++++++++++++++++++ tests/ui/single_match_else.stderr | 44 ++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b754a45aa40..a6cc1097441 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -530,16 +530,22 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp // the lint noisy in unnecessary situations return; } - let els = remove_blocks(&arms[1].body); - let els = if is_unit_expr(els) { + let els = arms[1].body; + let els = if is_unit_expr(remove_blocks(els)) { None - } else if let ExprKind::Block(_, _) = els.kind { - // matches with blocks that contain statements are prettier as `if let + else` - Some(els) + } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind { + if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { + // single statement/expr "else" block, don't lint + return; + } else { + // block with 2+ statements or 1 expr and 1+ statement + Some(els) + } } else { - // allow match arms with just expressions - return; + // not a block, don't lint + return; }; + let ty = cx.tables().expr_ty(ex); if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { check_single_match_single_pattern(cx, ex, arms, expr, els); diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 34193be0b75..b624a41a29b 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,4 +1,6 @@ #![warn(clippy::single_match_else)] +#![allow(clippy::needless_return)] +#![allow(clippy::no_effect)] enum ExprNode { ExprAddrOf, @@ -30,6 +32,55 @@ macro_rules! unwrap_addr { }; } +#[rustfmt::skip] fn main() { unwrap_addr!(ExprNode::Unicorns); + + // + // don't lint single exprs/statements + // + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => return, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return + }, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return; + }, + } + + // + // lint multiple exprs/statements "else" blocks + // + + // lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + println!("else block"); + return + }, + } + + // lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + println!("else block"); + return; + }, + } } diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 59861d46eb3..3a07c2ec542 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:12:5 + --> $DIR/single_match_else.rs:14:5 | LL | / match ExprNode::Butterflies { LL | | ExprNode::ExprAddrOf => Some(&NODE), @@ -19,5 +19,45 @@ LL | None LL | } | -error: aborting due to previous error +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:70:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL | if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL | println!("else block"); +LL | return +LL | } + | + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:79:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return; +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL | if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL | println!("else block"); +LL | return; +LL | } + | + +error: aborting due to 3 previous errors From c79c6888a5509184112b774a56cfc5ca9f9f1e2b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 9 Jul 2020 22:07:15 +0900 Subject: [PATCH 0276/1110] Fix a broken link in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f7bdcb1be7..69a734e4ee4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -245,7 +245,7 @@ this to work, you will need the fix of `git subtree` available [here][gitgitgadget-pr]. [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 -[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree +[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust ## Issue and PR triage From 298a1fa3bd8ec04350b1bff6a6d92e34abf2e198 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 26 Jun 2020 17:03:10 +0200 Subject: [PATCH 0277/1110] Move range_minus_one to pedantic This moves the range_minus_one lint to the pedantic category, so there will not be any warnings emitted by default. This should work around problems where the suggestion is impossible to resolve due to the range consumer only accepting a specific range implementation, rather than the `RangeBounds` trait (see #3307). While it is possible to work around this by extracting the boundary into a variable, I don't think clippy should encourage people to disable or work around lints, but instead the lints should be fixable. So hopefully this will help until a proper implementation checks what the range is used for. --- clippy_lints/src/ranges.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index c164ec9aaf1..dd608de5723 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -52,6 +52,11 @@ declare_clippy_lint! { /// exclusive ranges, because they essentially add an extra branch that /// LLVM may fail to hoist out of the loop. /// + /// This will cause a warning that cannot be fixed if the consumer of the + /// range only accepts a specific range type, instead of the generic + /// `RangeBounds` trait + /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). + /// /// **Example:** /// ```rust,ignore /// for x..(y+1) { .. } @@ -72,7 +77,10 @@ declare_clippy_lint! { /// **Why is this bad?** The code is more readable with an exclusive range /// like `x..y`. /// - /// **Known problems:** None. + /// **Known problems:** This will cause a warning that cannot be fixed if + /// the consumer of the range only accepts a specific range type, instead of + /// the generic `RangeBounds` trait + /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). /// /// **Example:** /// ```rust,ignore @@ -83,7 +91,7 @@ declare_clippy_lint! { /// for x..y { .. } /// ``` pub RANGE_MINUS_ONE, - complexity, + pedantic, "`x..=(y-1)` reads better as `x..y`" } From ba2a85dadc61bbfeb483ca0d05ddfda213da1329 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 3 Jul 2020 21:09:32 +0200 Subject: [PATCH 0278/1110] Run update_lints --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..4d9776018cf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1162,6 +1162,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), + LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), @@ -1382,7 +1383,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::PTR_ARG), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), - LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), @@ -1598,7 +1598,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(&precedence::PRECEDENCE), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..c20793ecd3e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1783,7 +1783,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "range_minus_one", - group: "complexity", + group: "pedantic", desc: "`x..=(y-1)` reads better as `x..y`", deprecation: None, module: "ranges", From 780a4c87de88d7f747c7847b71c90c8268fe4b66 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 10 Jul 2020 23:53:15 +0900 Subject: [PATCH 0279/1110] Fix typo --- clippy_lints/src/repeat_once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index af3c948ec82..436374d7c54 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -15,7 +15,7 @@ declare_clippy_lint! { /// - `.clone()` for `String` /// - `.to_vec()` for `slice` /// - /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind thi, `clone()` should be used. + /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used. /// /// **Known problems:** None. /// From b3c719608d2c969323714517837f0c68aca23d81 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 10 Jul 2020 17:23:03 +0200 Subject: [PATCH 0280/1110] Fix test failures --- tests/ui/range_plus_minus_one.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/range_plus_minus_one.rs b/tests/ui/range_plus_minus_one.rs index 3cfed4125b3..7d034117547 100644 --- a/tests/ui/range_plus_minus_one.rs +++ b/tests/ui/range_plus_minus_one.rs @@ -7,6 +7,7 @@ fn f() -> usize { } #[warn(clippy::range_plus_one)] +#[warn(clippy::range_minus_one)] fn main() { for _ in 0..2 {} for _ in 0..=2 {} From afa4148cc6dee0f9e0ca5b33f2511b9305d84fcb Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 10 Jul 2020 17:53:01 +0200 Subject: [PATCH 0281/1110] Fix tests a bit more --- tests/ui/range_plus_minus_one.fixed | 1 + tests/ui/range_plus_minus_one.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/ui/range_plus_minus_one.fixed b/tests/ui/range_plus_minus_one.fixed index 6b402114099..19b253b0fe2 100644 --- a/tests/ui/range_plus_minus_one.fixed +++ b/tests/ui/range_plus_minus_one.fixed @@ -7,6 +7,7 @@ fn f() -> usize { } #[warn(clippy::range_plus_one)] +#[warn(clippy::range_minus_one)] fn main() { for _ in 0..2 {} for _ in 0..=2 {} diff --git a/tests/ui/range_plus_minus_one.stderr b/tests/ui/range_plus_minus_one.stderr index f72943a04f2..fb4f1658597 100644 --- a/tests/ui/range_plus_minus_one.stderr +++ b/tests/ui/range_plus_minus_one.stderr @@ -1,5 +1,5 @@ error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:14:14 + --> $DIR/range_plus_minus_one.rs:15:14 | LL | for _ in 0..3 + 1 {} | ^^^^^^^^ help: use: `0..=3` @@ -7,25 +7,25 @@ LL | for _ in 0..3 + 1 {} = note: `-D clippy::range-plus-one` implied by `-D warnings` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:17:14 + --> $DIR/range_plus_minus_one.rs:18:14 | LL | for _ in 0..1 + 5 {} | ^^^^^^^^ help: use: `0..=5` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:20:14 + --> $DIR/range_plus_minus_one.rs:21:14 | LL | for _ in 1..1 + 1 {} | ^^^^^^^^ help: use: `1..=1` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:26:14 + --> $DIR/range_plus_minus_one.rs:27:14 | LL | for _ in 0..(1 + f()) {} | ^^^^^^^^^^^^ help: use: `0..=f()` error: an exclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:30:13 + --> $DIR/range_plus_minus_one.rs:31:13 | LL | let _ = ..=11 - 1; | ^^^^^^^^^ help: use: `..11` @@ -33,25 +33,25 @@ LL | let _ = ..=11 - 1; = note: `-D clippy::range-minus-one` implied by `-D warnings` error: an exclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:31:13 + --> $DIR/range_plus_minus_one.rs:32:13 | LL | let _ = ..=(11 - 1); | ^^^^^^^^^^^ help: use: `..11` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:32:13 + --> $DIR/range_plus_minus_one.rs:33:13 | LL | let _ = (1..11 + 1); | ^^^^^^^^^^^ help: use: `(1..=11)` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:33:13 + --> $DIR/range_plus_minus_one.rs:34:13 | LL | let _ = (f() + 1)..(f() + 1); | ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:37:14 + --> $DIR/range_plus_minus_one.rs:38:14 | LL | for _ in 1..ONE + ONE {} | ^^^^^^^^^^^^ help: use: `1..=ONE` From 1b3bc16533a3e701616648920603c10674eb653b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 11 Jul 2020 12:28:21 +0200 Subject: [PATCH 0282/1110] Fix out of bounds access by checking length equality BEFORE accessing by index. Fixes #5780 --- clippy_lints/src/unnested_or_patterns.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 169a486d1eb..502bf0c4279 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -400,8 +400,8 @@ fn extend_with_matching( /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? fn eq_pre_post(ps1: &[P], ps2: &[P], idx: usize) -> bool { - ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. - && ps1.len() == ps2.len() + ps1.len() == ps2.len() + && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) } From 2c5f8ab582badb5b45124bcab01597cae46c7e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 11 Jul 2020 23:42:56 +0200 Subject: [PATCH 0283/1110] fix phrase in new_lint issue template --- .github/ISSUE_TEMPLATE/new_lint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/new_lint.md b/.github/ISSUE_TEMPLATE/new_lint.md index 70445d7ef25..98fd0df685f 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.md +++ b/.github/ISSUE_TEMPLATE/new_lint.md @@ -12,7 +12,7 @@ labels: L-lint - Kind: *See for list of lint kinds* -*What benefit of this lint over old code?* +*What is the advantage of the recommended code over the original code* For example: - Remove bounce checking inserted by ... From f2419b9d6299fb07a1163b47dc273f64e0444a6c Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 14 Jul 2020 00:11:10 +0900 Subject: [PATCH 0284/1110] Refactoring to use `constant_context Use `constant_context`, `.is_str()` and `builtin_index()` to simplify. --- clippy_lints/src/repeat_once.rs | 54 +++------------------------------ 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 436374d7c54..3a0b3b1c257 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,12 +1,9 @@ -use crate::consts::{miri_to_const, Constant}; +use crate::consts::{constant_context, Constant}; use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -44,10 +41,11 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if_chain! { if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; if path.ident.name == sym!(repeat); - if is_once(cx, &args[1]) && !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.tables()).expr(&args[1]); + if !in_macro(args[0].span); then { let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0])); - if is_str(ty){ + if ty.is_str() { span_lint_and_sugg( cx, REPEAT_ONCE, @@ -57,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_slice(ty) { + } else if let Some(_) = ty.builtin_index() { span_lint_and_sugg( cx, REPEAT_ONCE, @@ -82,45 +80,3 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { } } } - -fn is_once<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { - match expr.kind { - ExprKind::Lit(ref lit) => { - if let LitKind::Int(ref lit_content, _) = lit.node { - *lit_content == 1 - } else { - false - } - }, - ExprKind::Path(rustc_hir::QPath::Resolved(None, path)) => { - if let Res::Def(DefKind::Const, def_id) = path.res { - let ty = cx.tcx.type_of(def_id); - let con = cx - .tcx - .const_eval_poly(def_id) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)) - .unwrap(); - let con = miri_to_const(con); - con == Some(Constant::Int(1)) - } else { - false - } - }, - _ => false, - } -} - -fn is_str(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Str => true, - _ => false, - } -} - -fn is_slice(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Slice(..) | ty::Array(..) => true, - _ => false, - } -} From ff796b6d7082d5b5c073797aba17f454eebe3359 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 19 Jun 2020 11:44:03 +0200 Subject: [PATCH 0285/1110] Rename collapsable_if fix suggestion to "collapse nested if block" The name "try" is confusing when shown as quick fix by rust-analyzer --- clippy_lints/src/collapsible_if.rs | 4 ++-- tests/ui/collapsible_else_if.stderr | 14 +++++++------- tests/ui/collapsible_if.stderr | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 8090f4673aa..42bff564de0 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -115,7 +115,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { COLLAPSIBLE_IF, block.span, "this `else { if .. }` block can be collapsed", - "try", + "collapse nested if block", snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), applicability, ); @@ -142,7 +142,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & let rhs = Sugg::ast(cx, check_inner, ".."); diag.span_suggestion( expr.span, - "try", + "collapse nested if block", format!( "if {} {}", lhs.and(&rhs), diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 28048999e8e..3d1c458879e 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -10,7 +10,7 @@ LL | | } | |_____^ | = note: `-D clippy::collapsible-if` implied by `-D warnings` -help: try +help: collapse nested if block | LL | } else if y == "world" { LL | println!("world!") @@ -28,7 +28,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world!") @@ -48,7 +48,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if y == "world" { LL | println!("world") @@ -71,7 +71,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") @@ -94,7 +94,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") @@ -117,7 +117,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if x == "hello" { LL | println!("world") @@ -140,7 +140,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 6440ff41be8..f56dd65b9dd 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::collapsible-if` implied by `-D warnings` -help: try +help: collapse nested if block | LL | if x == "hello" && y == "world" { LL | println!("Hello world!"); @@ -26,7 +26,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { LL | println!("Hello world!"); @@ -43,7 +43,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { LL | println!("Hello world!"); @@ -60,7 +60,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { LL | println!("Hello world!"); @@ -77,7 +77,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { LL | println!("Hello world!"); @@ -94,7 +94,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if 42 == 1337 && 'a' != 'A' { LL | println!("world!") @@ -111,7 +111,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && y == "world" { // Collapsible LL | println!("Hello world!"); From 201999ccfd18a9debe1f186f30f40659ebc6b933 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Mon, 13 Jul 2020 10:33:25 -0700 Subject: [PATCH 0286/1110] improve advice in iter_nth_zero The "use .next()" replacement advice is on the last line of the code snippet, where it is vulnerable to truncation. Display that advice at the beginning instead. closes #5783 --- clippy_lints/src/methods/mod.rs | 4 ++-- tests/ui/iter_nth_zero.stderr | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4c595029ff7..565a08f1292 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2348,8 +2348,8 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar cx, ITER_NTH_ZERO, expr.span, - "called `.nth(0)` on a `std::iter::Iterator`", - "try calling", + "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", + "try calling .next() instead of .nth(0)", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_nth_zero.stderr b/tests/ui/iter_nth_zero.stderr index 2b20a4ceb4a..6c4200a7905 100644 --- a/tests/ui/iter_nth_zero.stderr +++ b/tests/ui/iter_nth_zero.stderr @@ -1,22 +1,22 @@ -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling: `iter.next()` + | ^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter.next()` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling: `iter2.next()` + | ^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter2.next()` error: aborting due to 3 previous errors From b4091032abbadf586ea8c77bc547ec2ac403ef0a Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 14 Jul 2020 08:08:13 +0900 Subject: [PATCH 0287/1110] Use `.is_some()` not `Some(_)` --- clippy_lints/src/repeat_once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 3a0b3b1c257..a3af369e41e 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), Applicability::MachineApplicable, ); - } else if let Some(_) = ty.builtin_index() { + } else if ty.builtin_index().is_some() { span_lint_and_sugg( cx, REPEAT_ONCE, From d067d0352bfc5a6979f477bc96c969b040437618 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 14 Jul 2020 08:18:15 +0200 Subject: [PATCH 0288/1110] Add test for `needless_range_loop` issue Closes #2277 This was fixed when we fixed #2542. --- tests/ui/needless_range_loop2.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index 2ed1b09bece..a82b1159161 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -83,3 +83,13 @@ fn main() { println!("{}", arr[i]); } } + +mod issue2277 { + pub fn example(list: &[[f64; 3]]) { + let mut x: [f64; 3] = [10.; 3]; + + for i in 0..3 { + x[i] = list.iter().map(|item| item[i]).sum::(); + } + } +} From 126790999a128a880ca276c49afd2927a66ffbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 30 Mar 2020 11:02:14 +0200 Subject: [PATCH 0289/1110] new lint: Returning unit from closures expecting Ord This lint catches cases where the last statement of a closure expecting an instance of Ord has a trailing semi-colon. It compiles since the closure ends up return () which also implements Ord but causes unexpected results in cases such as sort_by_key. Fixes #5080 reprise: rebase, update and address all concerns --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unit_return_expecting_ord.rs | 177 ++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/unit_return_expecting_ord.rs | 36 ++++ tests/ui/unit_return_expecting_ord.stderr | 39 ++++ 6 files changed, 265 insertions(+) create mode 100644 clippy_lints/src/unit_return_expecting_ord.rs create mode 100644 tests/ui/unit_return_expecting_ord.rs create mode 100644 tests/ui/unit_return_expecting_ord.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d08b44ba40..1c927b5f83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1679,6 +1679,7 @@ Released 2018-09-13 [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 32e79317f82..7a4ca3902b3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -300,6 +300,7 @@ mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; +mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; mod unnested_or_patterns; @@ -826,6 +827,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, &unicode::ZERO_WIDTH_SPACE, + &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, @@ -891,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); + store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); @@ -1436,6 +1439,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), @@ -1692,6 +1696,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs new file mode 100644 index 00000000000..fceb885516b --- /dev/null +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -0,0 +1,177 @@ +use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that expect closures of type + /// Fn(...) -> Ord where the implemented closure returns the unit type. + /// The lint also suggests to remove the semi-colon at the end of the statement if present. + /// + /// **Why is this bad?** Likely, returning the unit type is unintentional, and + /// could simply be caused by an extra semi-colon. Since () implements Ord + /// it doesn't cause a compilation error. + /// This is the same reasoning behind the unit_cmp lint. + /// + /// **Known problems:** If returning unit is intentional, then there is no + /// way of specifying this without triggering needless_return lint + /// + /// **Example:** + /// + /// ```rust + /// let mut twins = vec!((1,1), (2,2)); + /// twins.sort_by_key(|x| { x.1; }); + /// ``` + pub UNIT_RETURN_EXPECTING_ORD, + correctness, + "fn arguments of type Fn(...) -> Ord returning the unit type ()." +} + +declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]); + +fn get_trait_predicates_for_trait_id<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + trait_id: Option, +) -> Vec> { + let mut preds = Vec::new(); + for (pred, _) in generics.predicates { + if_chain! { + if let PredicateKind::Trait(poly_trait_pred, _) = pred.kind(); + let trait_pred = cx.tcx.erase_late_bound_regions(&poly_trait_pred); + if let Some(trait_def_id) = trait_id; + if trait_def_id == trait_pred.trait_ref.def_id; + then { + preds.push(trait_pred); + } + } + } + preds +} + +fn get_projection_pred<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + pred: TraitPredicate<'tcx>, +) -> Option> { + generics.predicates.iter().find_map(|(proj_pred, _)| { + if let PredicateKind::Projection(proj_pred) = proj_pred.kind() { + let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred); + if projection_pred.projection_ty.substs == pred.trait_ref.substs { + return Some(projection_pred); + } + } + None + }) +} + +fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> { + let mut args_to_check = Vec::new(); + if let Some(def_id) = cx.tables().type_dependent_def_id(expr.hir_id) { + let fn_sig = cx.tcx.fn_sig(def_id); + let generics = cx.tcx.predicates_of(def_id); + let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let partial_ord_preds = + get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); + // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error + // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for `&[&rustc::ty::TyS<'_>]` + let inputs_output = cx.tcx.erase_late_bound_regions(&fn_sig.inputs_and_output()); + inputs_output + .iter() + .rev() + .skip(1) + .rev() + .enumerate() + .for_each(|(i, inp)| { + for trait_pred in &fn_mut_preds { + if_chain! { + if trait_pred.self_ty() == inp; + if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); + then { + if ord_preds.iter().any(|ord| ord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "Ord".to_string())); + } else if partial_ord_preds.iter().any(|pord| pord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "PartialOrd".to_string())); + } + } + } + } + }); + } + args_to_check +} + +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { + if_chain! { + if let ExprKind::Closure(_, _fn_decl, body_id, span, _) = arg.kind; + if let ty::Closure(_def_id, substs) = &cx.tables().node_type(arg.hir_id).kind; + let ret_ty = substs.as_closure().sig().output(); + let ty = cx.tcx.erase_late_bound_regions(&ret_ty); + if ty.is_unit(); + then { + if_chain! { + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = body.value.kind; + if block.expr.is_none(); + if let Some(stmt) = block.stmts.last(); + if let StmtKind::Semi(_) = stmt.kind; + then { + let data = stmt.span.data(); + // Make a span out of the semicolon for the help message + Some((span, Some(Span::new(data.hi-BytePos(1), data.hi, data.ctxt)))) + } else { + Some((span, None)) + } + } + } else { + None + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + let arg_indices = get_args_to_check(cx, expr); + for (i, trait_name) in arg_indices { + if i < args.len() { + match check_arg(cx, &args[i]) { + Some((span, None)) => { + span_lint( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + ); + }, + Some((span, Some(last_semi))) => { + span_lint_and_help( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + Some(last_semi), + &"probably caused by this trailing semicolon".to_string(), + ); + }, + None => {}, + } + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b89a8712862..96b004904aa 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2292,6 +2292,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "unit_return_expecting_ord", + group: "correctness", + desc: "fn arguments of type Fn(...) -> Ord returning the unit type ().", + deprecation: None, + module: "unit_return_expecting_ord", + }, Lint { name: "unknown_clippy_lints", group: "style", diff --git a/tests/ui/unit_return_expecting_ord.rs b/tests/ui/unit_return_expecting_ord.rs new file mode 100644 index 00000000000..bdb4710cc69 --- /dev/null +++ b/tests/ui/unit_return_expecting_ord.rs @@ -0,0 +1,36 @@ +#![warn(clippy::unit_return_expecting_ord)] +#![allow(clippy::needless_return)] +#![allow(clippy::unused_unit)] +#![feature(is_sorted)] + +struct Struct { + field: isize, +} + +fn double(i: isize) -> isize { + i * 2 +} + +fn unit(_i: isize) {} + +fn main() { + let mut structs = vec![Struct { field: 2 }, Struct { field: 1 }]; + structs.sort_by_key(|s| { + double(s.field); + }); + structs.sort_by_key(|s| double(s.field)); + structs.is_sorted_by_key(|s| { + double(s.field); + }); + structs.is_sorted_by_key(|s| { + if s.field > 0 { + () + } else { + return (); + } + }); + structs.sort_by_key(|s| { + return double(s.field); + }); + structs.sort_by_key(|s| unit(s.field)); +} diff --git a/tests/ui/unit_return_expecting_ord.stderr b/tests/ui/unit_return_expecting_ord.stderr new file mode 100644 index 00000000000..e63d5874609 --- /dev/null +++ b/tests/ui/unit_return_expecting_ord.stderr @@ -0,0 +1,39 @@ +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:18:25 + | +LL | structs.sort_by_key(|s| { + | ^^^ + | + = note: `-D clippy::unit-return-expecting-ord` implied by `-D warnings` +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:19:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:22:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + | +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:23:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:25:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:35:25 + | +LL | structs.sort_by_key(|s| unit(s.field)); + | ^^^ + +error: aborting due to 4 previous errors + From e83b3eb9930ab527451edaaa6f524acd26c1bf1c Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 14 Jul 2020 09:20:19 -0700 Subject: [PATCH 0290/1110] formatting nits --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/iter_nth_zero.stderr | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 565a08f1292..69e985cc0a4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2349,7 +2349,7 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar ITER_NTH_ZERO, expr.span, "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", - "try calling .next() instead of .nth(0)", + "try calling `.next()` instead of `.nth(0)`", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_nth_zero.stderr b/tests/ui/iter_nth_zero.stderr index 6c4200a7905..29c56f3a94f 100644 --- a/tests/ui/iter_nth_zero.stderr +++ b/tests/ui/iter_nth_zero.stderr @@ -2,7 +2,7 @@ error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` @@ -10,13 +10,13 @@ error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter.next()` + | ^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter.next()` error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter2.next()` + | ^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter2.next()` error: aborting due to 3 previous errors From a0640457a912720a5473d7feff40576a2f97df1e Mon Sep 17 00:00:00 2001 From: Leo Meira Vital Date: Mon, 13 Jul 2020 01:57:19 -0300 Subject: [PATCH 0291/1110] Removing snippet from SHADOW_UNRELATED message. --- clippy_lints/src/shadow.rs | 6 +----- tests/ui/shadow.stderr | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 194786c5c41..fab13c8c124 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -295,11 +295,7 @@ fn lint_shadow<'tcx>( cx, SHADOW_UNRELATED, pattern_span, - &format!( - "`{}` is shadowed by `{}`", - snippet(cx, pattern_span, "_"), - snippet(cx, expr.span, "..") - ), + &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")), |diag| { diag.span_note(expr.span, "initialization happens here"); diag.span_note(prev_span, "previous binding is here"); diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 7fa58cf7649..8a831375b41 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -104,7 +104,7 @@ note: previous binding is here LL | let x = (1, x); | ^ -error: `x` is shadowed by `y` +error: `x` is being shadowed --> $DIR/shadow.rs:34:9 | LL | let x = y; From ef896faa0153d0d96b6d6eafe7dd53b178525a25 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 16 Jul 2020 13:44:58 +0200 Subject: [PATCH 0292/1110] Fix deploy script for beta deployment Since the beta/ directory already exists, we can't copy the complete master dir --- .github/deploy.sh | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/deploy.sh b/.github/deploy.sh index 3f425e5b725..e0a95fb9f36 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -19,7 +19,7 @@ fi if [[ $BETA = "true" ]]; then echo "Update documentation for the beta release" - cp -r out/master out/beta + cp -r out/master/* out/beta fi # Generate version index that is shown as root index page @@ -33,12 +33,11 @@ cd out git config user.name "GHA CI" git config user.email "gha@ci.invalid" -if git diff --exit-code --quiet; then - echo "No changes to the output on this push; exiting." - exit 0 -fi - if [[ -n $TAG_NAME ]]; then + if git diff --exit-code --quiet -- $TAG_NAME/; then + echo "No changes to the output on this push; exiting." + exit 0 + fi # Add the new dir git add "$TAG_NAME" # Update the symlink @@ -47,9 +46,17 @@ if [[ -n $TAG_NAME ]]; then git add versions.json git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then + if git diff --exit-code --quiet -- beta/; then + echo "No changes to the output on this push; exiting." + exit 0 + fi git add beta git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}" else + if git diff --exit-code --quiet; then + echo "No changes to the output on this push; exiting." + exit 0 + fi git add . git commit -m "Automatic deploy to GitHub Pages: ${SHA}" fi From c65eb4d66314d22d85cf2e58ff20ec1ca7404751 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 16 Jul 2020 14:42:13 +0200 Subject: [PATCH 0293/1110] Track tag files, before checking for the diff --- .github/deploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/deploy.sh b/.github/deploy.sh index e0a95fb9f36..e85e8874ba6 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -34,6 +34,8 @@ git config user.name "GHA CI" git config user.email "gha@ci.invalid" if [[ -n $TAG_NAME ]]; then + # track files, so that the following check works + git add --intent-to-add "$TAG_NAME" if git diff --exit-code --quiet -- $TAG_NAME/; then echo "No changes to the output on this push; exiting." exit 0 From cf383cf48aa1bc09c50ae7d3599254c82b7dc0bf Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 16 Jul 2020 15:34:41 +0200 Subject: [PATCH 0294/1110] Update changelog to beta-1.46 --- CHANGELOG.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c927b5f83a..b9d0a15b755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,73 @@ document. ## Unreleased / In Rust Nightly -[7ea7cd1...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) +[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) + +## Rust 1.46 + +Current beta, release 2020-08-27 + +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) + +### New lints + +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378) +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597) +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623) +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637) + +### Moves and Deprecations + +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667) + +### Enhancements + +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695) + +### False Positive Fixes + +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled + [#5656](https://github.com/rust-lang/rust-clippy/pull/5656) +* [`let_and_return`]: Don't lint if a temporary borrow is involved + [#5680](https://github.com/rust-lang/rust-clippy/pull/5680) +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in + [#5692](https://github.com/rust-lang/rust-clippy/pull/5692) +* [`if_same_then_else`]: Don't assume multiplication is always commutative + [#5702](https://github.com/rust-lang/rust-clippy/pull/5702) +* [`blacklisted_name`]: Remove `bar` from the default configuration + [#5712](https://github.com/rust-lang/rust-clippy/pull/5712) +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts + [#5724](https://github.com/rust-lang/rust-clippy/pull/5724) + +### Suggestion Fixes/Improvements + +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code + [#4455](https://github.com/rust-lang/rust-clippy/pull/4455) +* Add auto applicable suggestion to [`macro_use_imports`] + [#5279](https://github.com/rust-lang/rust-clippy/pull/5279) + +### ICE Fixes + +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709) + +### Documentation Improvements + +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664) + +### Others + +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver` + into `rustc` and passes all the given arguments to `rustc`. This is especially + useful for tools that need the `rustc` version Clippy was compiled with, + instead of the Clippy version. E.g. `clippy-driver --rustc --version` will + print the output of `rustc --version`. + [#5178](https://github.com/rust-lang/rust-clippy/pull/5178) +* New issue templates now make it easier to complain if Clippy is too annoying + or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735) ## Rust 1.45 -Current beta, release 2020-07-16 +Current stable, released 2020-07-16 [891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1) @@ -87,7 +149,7 @@ and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/565 ## Rust 1.44 -Current stable, released 2020-06-04 +Released 2020-06-04 [204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8) From aba0d244d419261f94b3d942953412e35c3e857e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 16 Jul 2020 15:40:13 +0200 Subject: [PATCH 0295/1110] Typo: Change Log -> Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d0a15b755..776b04295f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Change Log +# Changelog All notable changes to this project will be documented in this file. See [Changelog Update](doc/changelog_update.md) if you want to update this From 70a41a92815a79c88dd9a2e8aa02503a3b95eae8 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 16 Jul 2020 16:51:12 -0700 Subject: [PATCH 0296/1110] Enable detecting multiple-argument panics --- clippy_lints/src/panic_unimplemented.rs | 9 ++-- tests/ui/panicking_macros.rs | 8 ++++ tests/ui/panicking_macros.stderr | 62 +++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 10f4694640e..9944b4096ba 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -96,23 +96,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { if_chain! { if let ExprKind::Block(ref block, _) = expr.kind; if let Some(ref ex) = block.expr; - if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC); - if params.len() == 1; + if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC) + .or(match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); then { + let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNIMPLEMENTED, span, "`unimplemented` should not be present in production code"); } else if is_expn_of(expr.span, "todo").is_some() { - let span = get_outer_span(expr); span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNREACHABLE, span, "`unreachable` should not be present in production code"); } else if is_expn_of(expr.span, "panic").is_some() { - let span = get_outer_span(expr); span_lint(cx, PANIC, span, "`panic` should not be present in production code"); match_panic(params, expr, cx); diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index dabb695368d..f91ccfaed74 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -4,24 +4,32 @@ fn panic() { let a = 2; panic!(); + panic!("message"); + panic!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn todo() { let a = 2; todo!(); + todo!("message"); + todo!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unimplemented() { let a = 2; unimplemented!(); + unimplemented!("message"); + unimplemented!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unreachable() { let a = 2; unreachable!(); + unreachable!("message"); + unreachable!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 72319bc7e45..37c11d72a57 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -6,29 +6,83 @@ LL | panic!(); | = note: `-D clippy::panic` implied by `-D warnings` +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:7:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:8:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:12:5 + --> $DIR/panicking_macros.rs:14:5 | LL | todo!(); | ^^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:15:5 + | +LL | todo!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:16:5 + | +LL | todo!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:22:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: `unimplemented` should not be present in production code + --> $DIR/panicking_macros.rs:23:5 + | +LL | unimplemented!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 | +LL | unimplemented!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:30:5 + | LL | unreachable!(); | ^^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` -error: aborting due to 4 previous errors +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:31:5 + | +LL | unreachable!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:32:5 + | +LL | unreachable!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors From 07867fde592babd74ee9934726c4c7010dee149b Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 16 Jul 2020 16:58:21 -0700 Subject: [PATCH 0297/1110] Clean up dogfood fallout --- clippy_lints/src/panic_unimplemented.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 9944b4096ba..6379dffd22e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { if let ExprKind::Block(ref block, _) = expr.kind; if let Some(ref ex) = block.expr; if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC) - .or(match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); + .or_else(|| match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); then { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { From 3618b97f59d9696c3e5ef83948269d0d7abfdc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 17 Jul 2020 01:58:41 +0200 Subject: [PATCH 0298/1110] fix typos (found by codespell) --- clippy_lints/src/deprecated_lints.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/inherent_impl.rs | 2 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/utils/numeric_literal.rs | 2 +- tests/ui/manual_async_fn.fixed | 2 +- tests/ui/manual_async_fn.rs | 2 +- tests/ui/manual_async_fn.stderr | 2 +- tests/ui/never_loop.rs | 2 +- tests/ui/precedence.fixed | 2 +- tests/ui/precedence.rs | 2 +- tests/ui/vec_resize_to_zero.rs | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 818d8188a78..c17a0e83330 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -153,7 +153,7 @@ declare_deprecated_lint! { /// /// **Deprecation reason:** Associated-constants are now preferred. pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants" + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" } declare_deprecated_lint! { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 323cad7fa1a..7a3f35aca0a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -10,7 +10,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. /// - /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise, + /// **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise, /// when not part of a method chain. /// /// **Example:** diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index bd7ca038839..9fb10c7f627 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { .. } = item.kind { - // Remember for each inherent implementation encoutered its span and generics + // Remember for each inherent implementation encountered its span and generics // but filter out implementations that have generic params (type or lifetime) // or are derived from a macro if !in_macro(item.span) && generics.params.is_empty() { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7a4ca3902b3..823afdfd289 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -463,7 +463,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_removed( "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants", + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", ); store.register_removed( "clippy::regex_macro", diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index bdce1bf1521..1ad184dfc46 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { - // If any of the params are dropable, return true + // If any of the params are droppable, return true param_tys.iter().any(|hir_ty| { let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); has_drop(cx, ty_ty) diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 59ccc6333fd..5041af750ce 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; declare_clippy_lint! { - /// **What it does:** Checks for modulo arithemtic. + /// **What it does:** Checks for modulo arithmetic. /// /// **Why is this bad?** The results of modulo (%) operation might differ /// depending on the language, when negative numbers are involved. diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 8dbe58763bf..9922d906118 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { } } -/// A struct containing information about occurences of the +/// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. struct OptionIfLetElseOccurence { option: String, diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 87cb454f654..5e8800d38eb 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,7 +36,7 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent seperator (b'e' or b'E') and the exponent part. + /// The character used as exponent separator (b'e' or b'E') and the exponent part. pub exponent: Option<(char, &'a str)>, /// The type suffix, including preceding underscore if present. diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 6bb1032a172..27222cc0869 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -30,7 +30,7 @@ async fn already_async() -> impl Future { 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 + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index d50c919188b..6a0f1b26c88 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -37,7 +37,7 @@ struct S {} impl S { fn inh_fut() -> impl Future { async { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index f278ee41aa3..a1904c904d0 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -57,7 +57,7 @@ LL | async fn inh_fut() -> i32 { help: move the body of the async block to the enclosing function | LL | fn inh_fut() -> impl Future { -LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | // NOTE: this code is here just to check that the indentation is correct in the suggested fix LL | let a = 42; LL | let b = 21; LL | if a < b { diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index cbc4ca39161..2770eb2b2ab 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -166,7 +166,7 @@ pub fn test14() { } } -// Issue #1991: the outter loop should not warn. +// Issue #1991: the outer loop should not warn. pub fn test15() { 'label: loop { while false { diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 17b1f1bd0bf..4d284ae1319 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d0891fd3c2..2d08e82f349 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs index 0263e2f5f20..7ed27439ec6 100644 --- a/tests/ui/vec_resize_to_zero.rs +++ b/tests/ui/vec_resize_to_zero.rs @@ -7,7 +7,7 @@ fn main() { // not applicable vec![1, 2, 3, 4, 5].resize(2, 5); - // applicable here, but only implemented for integer litterals for now + // applicable here, but only implemented for integer literals for now vec!["foo", "bar", "baz"].resize(0, "bar"); // not applicable From e5105e82d31b254a81f38fbf38a8043ba41bee3a Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 17 Jul 2020 22:51:57 +0900 Subject: [PATCH 0299/1110] Fix typo --- clippy_lints/src/vec_resize_to_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs index cc5e21a7ca6..7f90aedf161 100644 --- a/clippy_lints/src/vec_resize_to_zero.rs +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -11,7 +11,7 @@ use rustc_ast::ast::LitKind; use rustc_hir as hir; declare_clippy_lint! { - /// **What it does:** Finds occurences of `Vec::resize(0, an_int)` + /// **What it does:** Finds occurrences of `Vec::resize(0, an_int)` /// /// **Why is this bad?** This is probably an argument inversion mistake. /// From 7c5d4a41459abeb40eea734efaf08657602815cb Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 17 Jul 2020 09:27:43 -0700 Subject: [PATCH 0300/1110] Add test for correct behavior --- tests/ui/redundant_pattern_matching.fixed | 3 + tests/ui/redundant_pattern_matching.rs | 3 + tests/ui/redundant_pattern_matching.stderr | 68 ++++++++++++---------- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index ce8582d2b22..adbff8af8d9 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -11,6 +11,9 @@ )] fn main() { + let result: Result = Err(5); + if result.is_ok() {} + if Ok::(42).is_ok() {} if Err::(42).is_err() {} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index a3a9aa40e3b..4c2870e7803 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -11,6 +11,9 @@ )] fn main() { + let result: Result = Err(5); + if let Ok(_) = &result {} + if let Ok(_) = Ok::(42) {} if let Err(_) = Err::(42) {} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 25d1476062e..d3c9ceaa3d7 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,73 +1,79 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:15:12 | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` +LL | if let Ok(_) = &result {} + | -------^^^^^---------- help: try this: `if result.is_ok()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:17:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:18:12 + --> $DIR/redundant_pattern_matching.rs:21:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:20:12 + --> $DIR/redundant_pattern_matching.rs:23:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:22:12 + --> $DIR/redundant_pattern_matching.rs:25:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:31:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:33:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:32:15 + --> $DIR/redundant_pattern_matching.rs:35:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:34:15 + --> $DIR/redundant_pattern_matching.rs:37:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:36:15 + --> $DIR/redundant_pattern_matching.rs:39:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:39:15 + --> $DIR/redundant_pattern_matching.rs:42:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:55:5 + --> $DIR/redundant_pattern_matching.rs:58:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +82,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:60:5 + --> $DIR/redundant_pattern_matching.rs:63:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +91,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:65:5 + --> $DIR/redundant_pattern_matching.rs:68:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +100,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:70:5 + --> $DIR/redundant_pattern_matching.rs:73:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +109,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:75:5 + --> $DIR/redundant_pattern_matching.rs:78:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +118,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:80:5 + --> $DIR/redundant_pattern_matching.rs:83:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +127,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:85:13 + --> $DIR/redundant_pattern_matching.rs:88:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,64 +137,64 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:90:20 + --> $DIR/redundant_pattern_matching.rs:93:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:96:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:99:20 + --> $DIR/redundant_pattern_matching.rs:102:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:101:19 + --> $DIR/redundant_pattern_matching.rs:104:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:103:19 + --> $DIR/redundant_pattern_matching.rs:106:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:105:19 + --> $DIR/redundant_pattern_matching.rs:108:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:138:19 + --> $DIR/redundant_pattern_matching.rs:141:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:16 + --> $DIR/redundant_pattern_matching.rs:142:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:145:12 + --> $DIR/redundant_pattern_matching.rs:148:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:146:15 + --> $DIR/redundant_pattern_matching.rs:149:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors From e85b590936863e88da4ccd73af9f5da0787b9609 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 17 Jul 2020 10:40:01 -0700 Subject: [PATCH 0301/1110] Fix bug in lint --- clippy_lints/src/matches.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index bd474c20807..fe00a48adef 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1512,6 +1512,10 @@ mod redundant_pattern_match { } } + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, @@ -1524,7 +1528,7 @@ mod redundant_pattern_match { // while let ... = ... { ... } // ^^^ - let op_span = op.span.source_callsite(); + let op_span = result_expr.span.source_callsite(); // while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^ @@ -1589,17 +1593,21 @@ mod redundant_pattern_match { }; if let Some(good_method) = found_good_method { + let span = expr.span.to(op.span); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, expr.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { - let span = expr.span.to(op.span); diag.span_suggestion( span, "try this", - format!("{}.{}", snippet(cx, op.span, "_"), good_method), + format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method), Applicability::MaybeIncorrect, // snippet ); }, From 3d3a13d8719893972e2146cde86672151e7d5476 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 17 Jul 2020 21:39:05 +0200 Subject: [PATCH 0302/1110] Fix sync fallout (fmt) --- clippy_lints/src/non_expressive_names.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 1d4772bb3d6..48ab98418e4 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -218,12 +218,16 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { let mut split_at = None; match existing_name.len.cmp(&count) { Ordering::Greater => { - if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) { + if existing_name.len - count != 1 + || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) + { continue; } }, Ordering::Less => { - if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) { + if count - existing_name.len != 1 + || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) + { continue; } }, From 442c8ae23b90874485468b3becfc011f8c9d40bb Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 18 Jul 2020 23:59:34 +0200 Subject: [PATCH 0303/1110] Fix FP for `suspicious_arithmetic_impl` from `suspicious_trait_impl` lint --- clippy_lints/src/suspicious_trait_impl.rs | 37 +++++++++++------------ tests/ui/suspicious_arithmetic_impl.rs | 30 ++++++++++++++++++ 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 6d1d083fa8d..502fffc5e6c 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -64,26 +64,22 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { | hir::BinOpKind::Gt => return, _ => {}, } - // Check if the binary expression is part of another bi/unary expression - // or operator assignment as a child node - let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id); - while parent_expr != hir::CRATE_HIR_ID { - if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) { - match e.kind { - hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => return, - _ => {}, + + // Check for more than one binary operation in the implemented function + // Linting when multiple operations are involved can result in false positives + if_chain! { + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); + if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); + if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; + let body = cx.tcx.hir().body(body_id); + let mut visitor = BinaryExprVisitor { nb_binops: 0 }; + + then { + walk_expr(&mut visitor, &body.value); + if visitor.nb_binops > 1 { + return; } } - parent_expr = cx.tcx.hir().get_parent_node(parent_expr); - } - // as a parent node - let mut visitor = BinaryExprVisitor { in_binary_expr: false }; - walk_expr(&mut visitor, expr); - - if visitor.in_binary_expr { - return; } if let Some(impl_trait) = check_binop( @@ -181,7 +177,7 @@ fn check_binop( } struct BinaryExprVisitor { - in_binary_expr: bool, + nb_binops: u32, } impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { @@ -191,12 +187,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { match expr.kind { hir::ExprKind::Binary(..) | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true, + | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, _ => {}, } walk_expr(self, expr); } + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs index 1f5b9811887..60c2f3ec9b6 100644 --- a/tests/ui/suspicious_arithmetic_impl.rs +++ b/tests/ui/suspicious_arithmetic_impl.rs @@ -88,3 +88,33 @@ fn main() {} fn do_nothing(x: u32) -> u32 { x } + +struct MultipleBinops(u32); + +impl Add for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `add` impl + fn add(self, other: Self) -> Self::Output { + let mut result = self.0 + other.0; + if result >= u32::max_value() { + result -= u32::max_value(); + } + MultipleBinops(result) + } +} + +impl Mul for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `mul` impl + fn mul(self, other: Self) -> Self::Output { + let mut result: u32 = 0; + let size = std::cmp::max(self.0, other.0) as usize; + let mut v = vec![0; size + 1]; + for i in 0..size + 1 { + result *= i as u32; + } + MultipleBinops(result) + } +} From c720d823e1e633cccfd24e1df76cc04a628e83b0 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 14 Jul 2020 20:27:25 +0200 Subject: [PATCH 0304/1110] redundant_closure_call - don't lint when used more than once --- clippy_lints/src/misc_early.rs | 40 ++++++++++++++++++++------ tests/ui/redundant_closure_call.rs | 15 +++++++--- tests/ui/redundant_closure_call.stderr | 14 +++------ 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index b84a1a3fe24..125df226ceb 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -14,6 +14,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -493,6 +494,29 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + fn count_closure_usage(block: &Block, ident: &Ident) -> usize { + struct ClosureUsageCount<'ast> { + ident: &'ast Ident, + count: usize, + }; + impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> { + fn visit_expr(&mut self, expr: &'ast Expr) { + if_chain! { + if let ExprKind::Call(ref closure, _) = expr.kind; + if let ExprKind::Path(_, ref path) = closure.kind; + if self.ident == &path.segments[0].ident; + then { + self.count += 1; + } + } + walk_expr(self, expr); + } + } + let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + for w in block.stmts.windows(2) { if_chain! { if let StmtKind::Local(ref local) = w[0].kind; @@ -503,15 +527,15 @@ impl EarlyLintPass for MiscEarlyLints { if let ExprKind::Assign(_, ref call, _) = second.kind; if let ExprKind::Call(ref closure, _) = call.kind; if let ExprKind::Path(_, ref path) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(block, &ident) == 1; then { - if ident == path.segments[0].ident { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); } } } diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call.rs index bacd67db7c3..0f2ba4a075d 100644 --- a/tests/ui/redundant_closure_call.rs +++ b/tests/ui/redundant_closure_call.rs @@ -8,14 +8,21 @@ fn main() { k = (|a, b| a * b)(1, 5); - let closure = || 32; - i = closure(); - + // don't lint here, the closure is used more than once let closure = |i| i + 1; i = closure(3); - i = closure(4); + // lint here + let redun_closure = || 1; + i = redun_closure(); + + // the lint is applicable here but the lint doesn't support redefinition + let redefined_closure = || 1; + i = redefined_closure(); + let redefined_closure = || 2; + i = redefined_closure(); + #[allow(clippy::needless_return)] (|| return 2)(); (|| -> Option { None? })(); diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call.stderr index 68c1416bb6b..d5e0664319b 100644 --- a/tests/ui/redundant_closure_call.stderr +++ b/tests/ui/redundant_closure_call.stderr @@ -1,17 +1,11 @@ error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:12:5 + --> $DIR/redundant_closure_call.rs:18:5 | -LL | i = closure(); - | ^^^^^^^^^^^^^ +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:15:5 - | -LL | i = closure(3); - | ^^^^^^^^^^^^^^ - error: Try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call.rs:7:17 | @@ -24,5 +18,5 @@ error: Try not to call a closure in the expression where it is declared. LL | k = (|a, b| a * b)(1, 5); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 0fecaf1abcbe8d4ba1a9c532b69aab6fab3097c8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 19 Jul 2020 00:33:54 +0200 Subject: [PATCH 0305/1110] redundant_closure_call - extract lint from misc_early.rs, adapt to LatePass --- clippy_lints/src/lib.rs | 9 +- clippy_lints/src/misc_early.rs | 132 +-------------- clippy_lints/src/redundant_closure_call.rs | 151 ++++++++++++++++++ src/lintlist/mod.rs | 2 +- tests/ui/redundant_closure_call_early.rs | 19 +++ ...rr => redundant_closure_call_early.stderr} | 18 +-- ...call.rs => redundant_closure_call_late.rs} | 8 - tests/ui/redundant_closure_call_late.stderr | 10 ++ 8 files changed, 197 insertions(+), 152 deletions(-) create mode 100644 clippy_lints/src/redundant_closure_call.rs create mode 100644 tests/ui/redundant_closure_call_early.rs rename tests/ui/{redundant_closure_call.stderr => redundant_closure_call_early.stderr} (56%) rename tests/ui/{redundant_closure_call.rs => redundant_closure_call_late.rs} (72%) create mode 100644 tests/ui/redundant_closure_call_late.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 823afdfd289..f371942dbee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -276,6 +276,7 @@ mod ptr_offset_with_cast; mod question_mark; mod ranges; mod redundant_clone; +mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -702,7 +703,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &misc_early::DOUBLE_NEG, &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, &misc_early::MIXED_CASE_HEX_LITERALS, - &misc_early::REDUNDANT_CLOSURE_CALL, &misc_early::REDUNDANT_PATTERN, &misc_early::UNNEEDED_FIELD_PATTERN, &misc_early::UNNEEDED_WILDCARD_PATTERN, @@ -759,6 +759,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_ZIP_WITH_LEN, &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, + &redundant_closure_call::REDUNDANT_CLOSURE_CALL, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -1018,6 +1019,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box int_plus_one::IntPlusOne); store.register_early_pass(|| box formatting::Formatting); store.register_early_pass(|| box misc_early::MiscEarlyLints); + store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); + store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_early_pass(|| box returns::Return); store.register_late_pass(|| box let_and_return::LetReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); @@ -1359,7 +1362,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::DOUBLE_NEG), LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), @@ -1393,6 +1395,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), + LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), @@ -1593,7 +1596,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::USELESS_ASREF), LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&needless_bool::BOOL_COMPARISON), @@ -1608,6 +1610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&precedence::PRECEDENCE), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 125df226ceb..29aba7c1218 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -1,20 +1,15 @@ -use crate::utils::{ - constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, -}; -use if_chain::if_chain; +use crate::utils::{constants, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::{ - BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, StmtKind, UnOp, + BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, + NodeId, Pat, PatKind, UnOp, }; -use rustc_ast::visit::{walk_expr, FnKind, Visitor}; +use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -71,28 +66,6 @@ declare_clippy_lint! { "function arguments having names which only differ by an underscore" } -declare_clippy_lint! { - /// **What it does:** Detects closures called in the same expression where they - /// are defined. - /// - /// **Why is this bad?** It is unnecessarily adding to the expression's - /// complexity. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// // Bad - /// let a = (|| 42)() - /// - /// // Good - /// let a = 42 - /// ``` - pub REDUNDANT_CLOSURE_CALL, - complexity, - "throwaway closures called in the expression they are defined" -} - declare_clippy_lint! { /// **What it does:** Detects expressions of the form `--x`. /// @@ -279,7 +252,6 @@ declare_clippy_lint! { declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, - REDUNDANT_CLOSURE_CALL, DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX, @@ -289,30 +261,6 @@ declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_WILDCARD_PATTERN, ]); -// Used to find `return` statements or equivalents e.g., `?` -struct ReturnVisitor { - found_return: bool, -} - -impl ReturnVisitor { - #[must_use] - fn new() -> Self { - Self { found_return: false } - } -} - -impl<'ast> Visitor<'ast> for ReturnVisitor { - fn visit_expr(&mut self, ex: &'ast Expr) { - if let ExprKind::Ret(_) = ex.kind { - self.found_return = true; - } else if let ExprKind::Try(_) = ex.kind { - self.found_return = true; - } - - walk_expr(self, ex) - } -} - impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { @@ -454,30 +402,6 @@ impl EarlyLintPass for MiscEarlyLints { return; } match expr.kind { - ExprKind::Call(ref paren, _) => { - if let ExprKind::Paren(ref closure) = paren.kind { - if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind { - let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); - if !visitor.found_return { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_CALL, - expr.span, - "Try not to call a closure in the expression where it is declared.", - |diag| { - if decl.inputs.is_empty() { - let mut app = Applicability::MachineApplicable; - let hint = - snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); - } - }, - ); - } - } - } - }, ExprKind::Unary(UnOp::Neg, ref inner) => { if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { span_lint( @@ -492,54 +416,6 @@ impl EarlyLintPass for MiscEarlyLints { _ => (), } } - - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { - fn count_closure_usage(block: &Block, ident: &Ident) -> usize { - struct ClosureUsageCount<'ast> { - ident: &'ast Ident, - count: usize, - }; - impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> { - fn visit_expr(&mut self, expr: &'ast Expr) { - if_chain! { - if let ExprKind::Call(ref closure, _) = expr.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - if self.ident == &path.segments[0].ident; - then { - self.count += 1; - } - } - walk_expr(self, expr); - } - } - let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; - closure_usage_count.visit_block(block); - closure_usage_count.count - } - - for w in block.stmts.windows(2) { - if_chain! { - if let StmtKind::Local(ref local) = w[0].kind; - if let Option::Some(ref t) = local.init; - if let ExprKind::Closure(..) = t.kind; - if let PatKind::Ident(_, ident, _) = local.pat.kind; - if let StmtKind::Semi(ref second) = w[1].kind; - if let ExprKind::Assign(_, ref call, _) = second.kind; - if let ExprKind::Call(ref closure, _) = call.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - if ident == path.segments[0].ident; - if count_closure_usage(block, &ident) == 1; - then { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } - } - } - } } impl MiscEarlyLints { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs new file mode 100644 index 00000000000..391d70af159 --- /dev/null +++ b/clippy_lints/src/redundant_closure_call.rs @@ -0,0 +1,151 @@ +use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit as ast_visit; +use rustc_ast::visit::Visitor as AstVisitor; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit as hir_visit; +use rustc_hir::intravisit::Visitor as HirVisitor; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** Detects closures called in the same expression where they + /// are defined. + /// + /// **Why is this bad?** It is unnecessarily adding to the expression's + /// complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 + /// ``` + pub REDUNDANT_CLOSURE_CALL, + complexity, + "throwaway closures called in the expression they are defined" +} + +declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]); + +// Used to find `return` statements or equivalents e.g., `?` +struct ReturnVisitor { + found_return: bool, +} + +impl ReturnVisitor { + #[must_use] + fn new() -> Self { + Self { found_return: false } + } +} + +impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { + fn visit_expr(&mut self, ex: &'ast ast::Expr) { + if let ast::ExprKind::Ret(_) = ex.kind { + self.found_return = true; + } else if let ast::ExprKind::Try(_) = ex.kind { + self.found_return = true; + } + + ast_visit::walk_expr(self, ex) + } +} + +impl EarlyLintPass for RedundantClosureCall { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if_chain! { + if let ast::ExprKind::Call(ref paren, _) = expr.kind; + if let ast::ExprKind::Paren(ref closure) = paren.kind; + if let ast::ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind; + then { + let mut visitor = ReturnVisitor::new(); + visitor.visit_expr(block); + if !visitor.found_return { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_CALL, + expr.span, + "Try not to call a closure in the expression where it is declared.", + |diag| { + if decl.inputs.is_empty() { + let mut app = Applicability::MachineApplicable; + let hint = + snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); + diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + } + }, + ); + } + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, ident: &'tcx Ident) -> usize { + struct ClosureUsageCount<'tcx> { + ident: &'tcx Ident, + count: usize, + }; + impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if_chain! { + if let hir::ExprKind::Call(ref closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if self.ident == &path.segments[0].ident; + then { + self.count += 1; + } + } + hir_visit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::None + } + }; + let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + + for w in block.stmts.windows(2) { + if_chain! { + if let hir::StmtKind::Local(ref local) = w[0].kind; + if let Option::Some(ref t) = local.init; + if let hir::ExprKind::Closure(..) = t.kind; + if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; + if let hir::StmtKind::Semi(ref second) = w[1].kind; + if let hir::ExprKind::Assign(_, ref call, _) = second.kind; + if let hir::ExprKind::Call(ref closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(block, &ident) == 1; + then { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96b004904aa..1879aae77fb 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1835,7 +1835,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "complexity", desc: "throwaway closures called in the expression they are defined", deprecation: None, - module: "misc_early", + module: "redundant_closure_call", }, Lint { name: "redundant_closure_for_method_calls", diff --git a/tests/ui/redundant_closure_call_early.rs b/tests/ui/redundant_closure_call_early.rs new file mode 100644 index 00000000000..3dd365620cc --- /dev/null +++ b/tests/ui/redundant_closure_call_early.rs @@ -0,0 +1,19 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // lint here + let mut k = (|m| m + 1)(i); + + // lint here + k = (|a, b| a * b)(1, 5); + + // don't lint these + #[allow(clippy::needless_return)] + (|| return 2)(); + (|| -> Option { None? })(); + (|| -> Result { Err(2)? })(); +} diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call_early.stderr similarity index 56% rename from tests/ui/redundant_closure_call.stderr rename to tests/ui/redundant_closure_call_early.stderr index d5e0664319b..0ac97ae25d0 100644 --- a/tests/ui/redundant_closure_call.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,22 +1,16 @@ -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:18:5 +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call_early.rs:9:17 | -LL | i = redun_closure(); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut k = (|m| m + 1)(i); + | ^^^^^^^^^^^^^^ | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` error: Try not to call a closure in the expression where it is declared. - --> $DIR/redundant_closure_call.rs:7:17 - | -LL | let mut k = (|m| m + 1)(i); - | ^^^^^^^^^^^^^^ - -error: Try not to call a closure in the expression where it is declared. - --> $DIR/redundant_closure_call.rs:9:9 + --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call_late.rs similarity index 72% rename from tests/ui/redundant_closure_call.rs rename to tests/ui/redundant_closure_call_late.rs index 0f2ba4a075d..21ee58e5b44 100644 --- a/tests/ui/redundant_closure_call.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -4,9 +4,6 @@ fn main() { let mut i = 1; - let mut k = (|m| m + 1)(i); - - k = (|a, b| a * b)(1, 5); // don't lint here, the closure is used more than once let closure = |i| i + 1; @@ -22,9 +19,4 @@ fn main() { i = redefined_closure(); let redefined_closure = || 2; i = redefined_closure(); - - #[allow(clippy::needless_return)] - (|| return 2)(); - (|| -> Option { None? })(); - (|| -> Result { Err(2)? })(); } diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr new file mode 100644 index 00000000000..0aebc3c419e --- /dev/null +++ b/tests/ui/redundant_closure_call_late.stderr @@ -0,0 +1,10 @@ +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:15:5 + | +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: aborting due to previous error + From 9603d9652b9d172842c77f566121437768bc962f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 19 Jul 2020 11:47:32 +0200 Subject: [PATCH 0306/1110] redundant_closure_call - add support for shadowed closures --- clippy_lints/src/redundant_closure_call.rs | 12 ++++++------ tests/ui/redundant_closure_call_late.rs | 15 ++++++++++----- tests/ui/redundant_closure_call_late.stderr | 14 +++++++++++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 391d70af159..9477e79f567 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -11,7 +11,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintCon use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Detects closures called in the same expression where they @@ -96,9 +95,9 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, ident: &'tcx Ident) -> usize { + fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { struct ClosureUsageCount<'tcx> { - ident: &'tcx Ident, + path: &'tcx hir::Path<'tcx>, count: usize, }; impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { @@ -108,7 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if_chain! { if let hir::ExprKind::Call(ref closure, _) = expr.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; - if self.ident == &path.segments[0].ident; + if self.path.segments[0].ident == path.segments[0].ident + && self.path.res == path.res; then { self.count += 1; } @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { hir_visit::NestedVisitorMap::None } }; - let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, &ident) == 1; + if count_closure_usage(block, path) == 1; then { span_lint( cx, diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index 21ee58e5b44..e29a1dce0c7 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -14,9 +14,14 @@ fn main() { let redun_closure = || 1; i = redun_closure(); - // the lint is applicable here but the lint doesn't support redefinition - let redefined_closure = || 1; - i = redefined_closure(); - let redefined_closure = || 2; - i = redefined_closure(); + // shadowed closures are supported, lint here + let shadowed_closure = || 1; + i = shadowed_closure(); + let shadowed_closure = || 2; + i = shadowed_closure(); + + // don't lint here + let shadowed_closure = || 2; + i = shadowed_closure(); + i = shadowed_closure(); } diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index 0aebc3c419e..0e000fee85a 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -6,5 +6,17 @@ LL | i = redun_closure(); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: aborting due to previous error +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:19:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:21:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors From 1ac8b85c9fd38ff0a14061cae0b4d31e9bfafd56 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 20 Jul 2020 00:36:31 +0200 Subject: [PATCH 0307/1110] redundant_closure_call - pr review --- clippy_lints/src/redundant_closure_call.rs | 8 ++++---- tests/ui/redundant_closure_call_early.stderr | 4 ++-- tests/ui/redundant_closure_call_fixable.stderr | 4 ++-- tests/ui/redundant_closure_call_late.stderr | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 9477e79f567..8aa478ea2d6 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,13 +77,13 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "Try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared.", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; let hint = snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + diag.span_suggestion(expr.span, "try doing something like", hint, app); } }, ); @@ -136,13 +136,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(block, path) == 1; then { span_lint( cx, REDUNDANT_CLOSURE_CALL, second.span, - "Closure called just once immediately after it was declared", + "closure called just once immediately after it was declared", ); } } diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 0ac97ae25d0..79f27663461 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index e7737f9dd85..644161d9f5d 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,8 +1,8 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); - | ^^^^^^^^^ help: Try doing something like: : `42` + | ^^^^^^^^^ help: try doing something like: `42` | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index 0e000fee85a..7c8865f1bd3 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -1,4 +1,4 @@ -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:15:5 | LL | i = redun_closure(); @@ -6,13 +6,13 @@ LL | i = redun_closure(); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:19:5 | LL | i = shadowed_closure(); | ^^^^^^^^^^^^^^^^^^^^^^ -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:21:5 | LL | i = shadowed_closure(); From a5cdd4aeb11fad6b0bf73d342398700a27c4484b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 20 Jul 2020 00:00:00 +0000 Subject: [PATCH 0308/1110] Ignore not really redundant clones of ManuallyDrop "Redundant" clones of `ManuallyDrop` are sometimes used for the side effect of invoking the clone, without running the drop implementation of the inner type. In other words, they aren't really redundant. For example, futures-rs crate: ```rust #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. unsafe fn increase_refcount(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop let arc = mem::ManuallyDrop::new(Arc::::from_raw(data as *const T)); // Now increase refcount, but don't drop new refcount either let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); } ``` Ignore redundant clone lint for ManuallyDrop. --- clippy_lints/src/redundant_clone.rs | 6 +++++ clippy_lints/src/utils/paths.rs | 1 + tests/ui/redundant_clone.fixed | 15 ++++++++++++ tests/ui/redundant_clone.rs | 15 ++++++++++++ tests/ui/redundant_clone.stderr | 38 ++++++++++++++--------------- 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index fda7480194d..7932be0d4b1 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -124,6 +124,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } + if let ty::Adt(ref def, _) = arg_ty.kind { + if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { + continue; + } + } + // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }` let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 4c3462802e9..a515ee29c82 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -59,6 +59,7 @@ pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "Link pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; +pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"]; pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 764c10a6d39..cdeefda4c23 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 839747b131d..acb7ffb305f 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index eced198283c..89b39254299 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -108,61 +108,61 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:61:22 + --> $DIR/redundant_clone.rs:62:22 | LL | (a.clone(), a.clone()) | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:61:21 + --> $DIR/redundant_clone.rs:62:21 | LL | (a.clone(), a.clone()) | ^ -error: redundant clone - --> $DIR/redundant_clone.rs:121:15 - | -LL | let _s = s.clone(); - | ^^^^^^^^ help: remove this - | -note: this value is dropped without further use - --> $DIR/redundant_clone.rs:121:14 - | -LL | let _s = s.clone(); - | ^ - error: redundant clone --> $DIR/redundant_clone.rs:122:15 | -LL | let _t = t.clone(); +LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use --> $DIR/redundant_clone.rs:122:14 | +LL | let _s = s.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:123:15 + | +LL | let _t = t.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:123:14 + | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:132:19 + --> $DIR/redundant_clone.rs:133:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:132:18 + --> $DIR/redundant_clone.rs:133:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:144:14 + --> $DIR/redundant_clone.rs:145:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:144:13 + --> $DIR/redundant_clone.rs:145:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ From 142a273441d1cf2039bd2857ee57cfa402e2a3f7 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 22 Jul 2020 05:23:55 +0900 Subject: [PATCH 0309/1110] Use `(std::)f64::EPSILON` in the examples as suggested in the lints --- clippy_lints/src/misc.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index fc10e5077b8..26a1c32b6b3 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -99,7 +99,9 @@ declare_clippy_lint! { /// if y != x {} // where both are floats /// /// // Good - /// let error = 0.01f64; // Use an epsilon for comparison + /// let error = f64::EPSILON; // Use an epsilon for comparison + /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. + /// // let error = std::f64::EPSILON; /// if (y - 1.23f64).abs() < error { } /// if (y - x).abs() > error { } /// ``` @@ -237,10 +239,12 @@ declare_clippy_lint! { /// const ONE: f64 = 1.00; /// /// // Bad - /// if x == ONE { } // where both are floats + /// if x == ONE { } // where both are floats /// /// // Good - /// let error = 0.1f64; // Use an epsilon for comparison + /// let error = f64::EPSILON; // Use an epsilon for comparison + /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. + /// // let error = std::f64::EPSILON; /// if (x - ONE).abs() < error { } /// ``` pub FLOAT_CMP_CONST, From 51f2a6f8b6eea9ebefddff39e87a1ca16c59827c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 22 Jul 2020 16:39:58 +0200 Subject: [PATCH 0310/1110] Add documentation for basic Clippy hacking --- CONTRIBUTING.md | 22 ++++----- doc/adding_lints.md | 7 +-- doc/basics.md | 106 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 doc/basics.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 69a734e4ee4..dfc5cc077c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,7 +32,7 @@ High level approach: 1. Find something to fix/improve 2. Change code (likely some file in `clippy_lints/src/`) -3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script +3. Follow the instructions in the [Basics docs](doc/basics.md) such as running the `setup-toolchain.sh` script 4. Run `cargo test` in the root directory and wiggle code until it passes 5. Open a PR (also can be done after 2. if you run into problems) @@ -95,16 +95,16 @@ quick read. ## Getting code-completion for rustc internals to work -Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals -using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not -available via a `rustup` component at the time of writing. -To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via -`git clone https://github.com/rust-lang/rust/`. -Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies -which rust-analyzer will be able to understand. -Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo -you just cloned. -The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to +Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not +available via a `rustup` component at the time of writing. +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via +`git clone https://github.com/rust-lang/rust/`. +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies +which rust-analyzer will be able to understand. +Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo +you just cloned. +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. Just make sure to remove the dependencies again before finally making a pull request! diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 8092be277cc..d5f4f5d5659 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -27,10 +27,7 @@ because that's clearly a non-descriptive name. ## Setup -When working on Clippy, you will need the current git master version of rustc, -which can change rapidly. Make sure you're working near rust-clippy's master, -and use the `setup-toolchain.sh` script to configure the appropriate toolchain -for the Clippy directory. +See the [Basics](basics.md#get-the-code) documentation. ## Getting Started @@ -113,7 +110,7 @@ For cargo lints, the process of testing differs in that we are interested in the `Cargo.toml` manifest file. We also need a minimal crate associated with that manifest. -If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` we will find by default two new crates, each with its manifest file: * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. diff --git a/doc/basics.md b/doc/basics.md new file mode 100644 index 00000000000..c1fd2fbcd1b --- /dev/null +++ b/doc/basics.md @@ -0,0 +1,106 @@ +# Basics for hacking on Clippy + +This document explains the basics for hacking on Clippy. Besides others, this +includes how to set-up the development environment, how to build and how to test +Clippy. For a more in depth description on the codebase take a look at [Adding +Lints] or [Common Tools]. + +[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md +[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md + +- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) + - [Get the code](#get-the-code) + - [Setup](#setup) + - [Building and Testing](#building-and-testing) + - [`cargo dev`](#cargo-dev) + +## Get the Code + +First, make sure you have checked out the latest version of Clippy. If this is +your first time working on Clippy, create a fork of the repository and clone it +afterwards with the following command: + +```bash +git clone git@github.com:/rust-clippy +``` + +If you've already cloned Clippy in the past, update it to the latest version: + +```bash +# upstream has to be the remote of the rust-lang/rust-clippy repo +git fetch upstream +# make sure that you are on the master branch +git checkout master +# rebase your master branch on the upstream master +git rebase upstream/master +# push to the master branch of your fork +git push +``` + +## Setup + +Next we need to setup the toolchain to compile Clippy. Since Clippy heavily +relies on compiler internals it is build with the latest rustc master. To get +this toolchain, you can just use the `setup-toolchain.sh` script or use +`rustup-toolchain-install-master`: + +```bash +sh setup-toolchain.sh +# OR +cargo install rustup-toolchain-install-master +rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools +rustup override set master +``` + +## Building and Testing + +Once the `master` toolchain is installed, you can build and test Clippy like +every other Rust project: + +```bash +cargo build # builds Clippy +cargo test # tests Clippy +``` + +Since Clippys test suite is pretty big, there are some commands that only run a +subset of Clippys tests: + +```bash +# only run UI tests +cargo uitest +# only run UI tests starting with `test_` +TESTNAME="test_" cargo uitest +# only run dogfood tests +cargo test --test dogfood +``` + +If the output of a UI test differs from the expected output, you can update the +reference file with: + +```bash +sh tests/ui/update-all-references.sh +``` + +For example, this is necessary, if you fix a typo in an error message of a lint +or if you modify a test file to add a test case. + +_Note:_ This command may update more files than you intended. In that case only +commit the files you wanted to update. + +## `cargo dev` + +Clippy has some dev tools to make working on Clippy more convenient. These tools +can be accessed through the `cargo dev` command. Available tools are listed +below. To get more information about these commands, just call them with +`--help`. + +```bash +# formats the whole Clippy codebase and all tests +cargo dev fmt +# register or update lint names/groups/... +cargo dev update_lints +# create a new lint and register it +cargo dev new_lint +# (experimental) Setup Clippy to work with rust-analyzer +cargo dev ra-setup +``` From bdc01c882e5f212c9a8a0384aef1b670122a4337 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 22 Jul 2020 18:07:33 +0200 Subject: [PATCH 0311/1110] Update Usage section of README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 222b81023a7..a2984d73641 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,8 @@ Table of contents: ## Usage -Since this is a tool for helping the developer of a library or application -write better code, it is recommended not to include Clippy as a hard dependency. -Options include using it as an optional dependency, as a cargo subcommand, or -as an included feature during build. These options are detailed below. +Below are instructions on how to use Clippy as a subcommand, compiled from source +or in Travis CI. ### As a cargo subcommand (`cargo clippy`) From 17903f6d7107c6d31ee15f4c46af29d1f4aa363f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 24 Jul 2020 17:48:43 +0200 Subject: [PATCH 0312/1110] Mention lint naming guidelines earlier --- doc/adding_lints.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index d5f4f5d5659..168092f7329 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -35,12 +35,14 @@ There is a bit of boilerplate code that needs to be set up when creating a new lint. Fortunately, you can use the clippy dev tools to handle this for you. We are naming our new lint `foo_functions` (lints are generally written in snake case), and we don't need type information so it will have an early pass type -(more on this later on). To get started on this lint you can run -`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` -(category will default to nursery if not provided). This command will create -two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`, -as well as run `cargo dev update_lints` to register the new lint. For cargo lints, -two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. +(more on this later on). If you're not sure if the name you chose fits the lint, +take a look at our [lint naming guidelines][lint_naming]. To get started on this +lint you can run `cargo dev new_lint --name=foo_functions --pass=early +--category=pedantic` (category will default to nursery if not provided). This +command will create two files: `tests/ui/foo_functions.rs` and +`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to +register the new lint. For cargo lints, two project hierarchies (fail/pass) will +be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! From 3a4cc9f7f085e73fbfe57e8c896b90a5fe61c4f4 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 24 Jul 2020 23:17:52 +0200 Subject: [PATCH 0313/1110] Address review comments --- doc/basics.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/basics.md b/doc/basics.md index c1fd2fbcd1b..5c07d9b98a5 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -48,6 +48,7 @@ this toolchain, you can just use the `setup-toolchain.sh` script or use sh setup-toolchain.sh # OR cargo install rustup-toolchain-install-master +# For better IDE integration also add `-c rustfmt -c rust-src` (optional) rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools rustup override set master ``` @@ -62,8 +63,8 @@ cargo build # builds Clippy cargo test # tests Clippy ``` -Since Clippys test suite is pretty big, there are some commands that only run a -subset of Clippys tests: +Since Clippy's test suite is pretty big, there are some commands that only run a +subset of Clippy's tests: ```bash # only run UI tests @@ -74,7 +75,7 @@ TESTNAME="test_" cargo uitest cargo test --test dogfood ``` -If the output of a UI test differs from the expected output, you can update the +If the output of a [UI test] differs from the expected output, you can update the reference file with: ```bash @@ -87,6 +88,8 @@ or if you modify a test file to add a test case. _Note:_ This command may update more files than you intended. In that case only commit the files you wanted to update. +[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests + ## `cargo dev` Clippy has some dev tools to make working on Clippy more convenient. These tools From b375f1dd20bf07175ec06d13e1e9dc8b20287cd3 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 25 Jul 2020 17:09:44 +0300 Subject: [PATCH 0314/1110] Add suggestion for `iter_skip_next` lint --- clippy_lints/src/methods/mod.rs | 24 ++++++++++++++---------- tests/ui/iter_skip_next.fixed | 22 ++++++++++++++++++++++ tests/ui/iter_skip_next.rs | 8 ++++---- tests/ui/iter_skip_next.stderr | 23 ++++++++--------------- 4 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 tests/ui/iter_skip_next.fixed diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 97cc58023f5..56686f6d24f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1408,7 +1408,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), - ["next", "skip"] => lint_iter_skip_next(cx, expr), + ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), @@ -2436,17 +2436,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: ); } -fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { +fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) { // lint if caller of skip is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - span_lint_and_help( - cx, - ITER_SKIP_NEXT, - expr.span, - "called `skip(x).next()` on an iterator", - None, - "this is more succinctly expressed by calling `nth(x)`", - ); + if let [caller, n] = skip_args { + let hint = format!(".nth({})", snippet(cx, n.span, "..")); + span_lint_and_sugg( + cx, + ITER_SKIP_NEXT, + expr.span.trim_start(caller.span).unwrap(), + "called `skip(x).next()` on an iterator", + "use `nth` instead", + hint, + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed new file mode 100644 index 00000000000..928b6acb951 --- /dev/null +++ b/tests/ui/iter_skip_next.fixed @@ -0,0 +1,22 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_skip_next)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn main() { + let some_vec = vec![0, 1, 2, 3]; + let _ = some_vec.iter().nth(42); + let _ = some_vec.iter().cycle().nth(42); + let _ = (1..10).nth(10); + let _ = &some_vec[..].iter().nth(3); + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip(42).next(); + let _ = foo.filter().skip(42).next(); +} diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs index a65ca3bbb13..7075e2598eb 100644 --- a/tests/ui/iter_skip_next.rs +++ b/tests/ui/iter_skip_next.rs @@ -1,15 +1,17 @@ +// run-rustfix // aux-build:option_helpers.rs #![warn(clippy::iter_skip_next)] #![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; /// Checks implementation of `ITER_SKIP_NEXT` lint -fn iter_skip_next() { - let mut some_vec = vec![0, 1, 2, 3]; +fn main() { + let some_vec = vec![0, 1, 2, 3]; let _ = some_vec.iter().skip(42).next(); let _ = some_vec.iter().cycle().skip(42).next(); let _ = (1..10).skip(10).next(); @@ -18,5 +20,3 @@ fn iter_skip_next() { let _ = foo.skip(42).next(); let _ = foo.filter().skip(42).next(); } - -fn main() {} diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index 5709f335529..feedc2f288a 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,35 +1,28 @@ error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:13:13 + --> $DIR/iter_skip_next.rs:15:28 | LL | let _ = some_vec.iter().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` | = note: `-D clippy::iter-skip-next` implied by `-D warnings` - = help: this is more succinctly expressed by calling `nth(x)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:14:13 + --> $DIR/iter_skip_next.rs:16:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:15:13 + --> $DIR/iter_skip_next.rs:17:20 | LL | let _ = (1..10).skip(10).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:14 + --> $DIR/iter_skip_next.rs:18:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` error: aborting due to 4 previous errors From c81bbd05b95ae03da9af4e7e25e3784edd039465 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 25 Jul 2020 23:58:22 +0900 Subject: [PATCH 0315/1110] Fix FP `useless_conversion` Fix #5833. --- clippy_lints/src/useless_conversion.rs | 11 +++++++++-- tests/ui/useless_conversion.fixed | 9 +++++++++ tests/ui/useless_conversion.rs | 9 +++++++++ tests/ui/useless_conversion.stderr | 14 +++++++------- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a48ad3185e9..1bf37632e32 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,6 +1,6 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, - span_lint_and_help, span_lint_and_sugg, + get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, + snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -79,6 +79,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if let Some(parent_expr) = get_parent_expr(cx, e) { + if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if &*parent_name.ident.as_str() != "into_iter" { + return; + } + } + } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); if TyS::same_type(a, b) { diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index fdd4bc581f3..813cdaecaa9 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 4cae745e7c0..540fea23b36 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 84ec5370278..b958b035452 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -23,43 +23,43 @@ LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:51:21 + --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:52:21 + --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:53:13 + --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:54:13 + --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:55:13 + --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:56:13 + --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:57:21 + --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` From 5a644964fc05752a1283dab238b81de7583f7d03 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 20:40:57 -0600 Subject: [PATCH 0316/1110] run cargo dev new_lint specifically: cargo dev new_lint --name derive_ord_xor_partial_ord --category correctness --pass late --- CHANGELOG.md | 1 + .../src/derive_ord_xor_partial_ord.rs | 28 +++++++++++++++++++ clippy_lints/src/lib.rs | 4 +++ src/lintlist/mod.rs | 7 +++++ tests/ui/derive_ord_xor_partial_ord.rs | 5 ++++ 5 files changed, 45 insertions(+) create mode 100644 clippy_lints/src/derive_ord_xor_partial_ord.rs create mode 100644 tests/ui/derive_ord_xor_partial_ord.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..a1780725044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1454,6 +1454,7 @@ Released 2018-09-13 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive_ord_xor_partial_ord.rs new file mode 100644 index 00000000000..7913aab6f24 --- /dev/null +++ b/clippy_lints/src/derive_ord_xor_partial_ord.rs @@ -0,0 +1,28 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "default lint description" +} + +declare_lint_pass!(DeriveOrdXorPartialOrd => [DERIVE_ORD_XOR_PARTIAL_ORD]); + +impl LateLintPass<'_, '_> for DeriveOrdXorPartialOrd {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..6d6dd06cc21 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,6 +173,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod derive_ord_xor_partial_ord; mod doc; mod double_comparison; mod double_parens; @@ -515,6 +516,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_HASH_XOR_EQ, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1230,6 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&double_comparison::DOUBLE_COMPARISONS), @@ -1648,6 +1651,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&drop_bounds::DROP_BOUNDS), LintId::of(&drop_forget_ref::DROP_COPY), LintId::of(&drop_forget_ref::DROP_REF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..00d3df8f94f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -360,6 +360,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "derive_ord_xor_partial_ord", + group: "correctness", + desc: "default lint description", + deprecation: None, + module: "derive_ord_xor_partial_ord", + }, Lint { name: "diverging_sub_expression", group: "complexity", diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs new file mode 100644 index 00000000000..63687e7b3db --- /dev/null +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -0,0 +1,5 @@ +#![warn(clippy::derive_ord_xor_partial_ord)] + +fn main() { + // test code goes here +} From fc20ee63a105c0df78113126e8749f5958d7dc47 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 20:54:04 -0600 Subject: [PATCH 0317/1110] move derive_ord_xor_partial_ord into derive mod so we can reuse derive_hash_xor_partial_eq code later --- clippy_lints/src/derive.rs | 23 ++++++++++++++- .../src/derive_ord_xor_partial_ord.rs | 28 ------------------- clippy_lints/src/lib.rs | 7 ++--- src/lintlist/mod.rs | 2 +- 4 files changed, 26 insertions(+), 34 deletions(-) delete mode 100644 clippy_lints/src/derive_ord_xor_partial_ord.rs diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 59c62f1ae94..627475ee1d9 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -43,6 +43,27 @@ declare_clippy_lint! { "deriving `Hash` but implementing `PartialEq` explicitly" } +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "default lint description" +} + declare_clippy_lint! { /// **What it does:** Checks for explicit `Clone` implementations for `Copy` /// types. @@ -103,7 +124,7 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]); +declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE]); impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { diff --git a/clippy_lints/src/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive_ord_xor_partial_ord.rs deleted file mode 100644 index 7913aab6f24..00000000000 --- a/clippy_lints/src/derive_ord_xor_partial_ord.rs +++ /dev/null @@ -1,28 +0,0 @@ -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; - -declare_clippy_lint! { - /// **What it does:** - /// - /// **Why is this bad?** - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub DERIVE_ORD_XOR_PARTIAL_ORD, - correctness, - "default lint description" -} - -declare_lint_pass!(DeriveOrdXorPartialOrd => [DERIVE_ORD_XOR_PARTIAL_ORD]); - -impl LateLintPass<'_, '_> for DeriveOrdXorPartialOrd {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6d6dd06cc21..996aad31d3e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,7 +173,6 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; -mod derive_ord_xor_partial_ord; mod doc; mod double_comparison; mod double_parens; @@ -514,9 +513,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, &derive::DERIVE_HASH_XOR_EQ, + &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, - &derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1232,7 +1231,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&double_comparison::DOUBLE_COMPARISONS), @@ -1651,7 +1650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&drop_bounds::DROP_BOUNDS), LintId::of(&drop_forget_ref::DROP_COPY), LintId::of(&drop_forget_ref::DROP_REF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 00d3df8f94f..011504710e1 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -365,7 +365,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "correctness", desc: "default lint description", deprecation: None, - module: "derive_ord_xor_partial_ord", + module: "derive", }, Lint { name: "diverging_sub_expression", From 0722991b62fd6e4d7d7a51425274f3288bcc96bc Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 21:36:50 -0600 Subject: [PATCH 0318/1110] add test for derive_ord_xor_partial_ord based on test for derive_hash_xor_partial_eq --- tests/ui/derive_ord_xor_partial_ord.rs | 67 +++++++++++++++++++++- tests/ui/derive_ord_xor_partial_ord.stderr | 1 + 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/ui/derive_ord_xor_partial_ord.stderr diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 63687e7b3db..15f66b7a9c5 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,5 +1,68 @@ #![warn(clippy::derive_ord_xor_partial_ord)] -fn main() { - // test code goes here +use std::cmp::Ordering; + +#[derive(PartialOrd, Ord, PartialEq, Eq)] +struct DeriveBoth; + +impl PartialEq for DeriveBoth { + fn eq(&self, _: &u64) -> bool { + true + } } + +impl PartialOrd for DeriveBoth { + fn partial_cmp(&self, _: &u64) -> Option { + Some(Ordering::Equal) + } +} + +#[derive(Ord, PartialEq, Eq)] +struct DeriveOrd; + +impl PartialOrd for DeriveOrd { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.cmp(self)) + } +} + +#[derive(Ord, PartialEq, Eq)] +struct DeriveOrdWithExplicitTypeVariable; + +impl PartialOrd for DeriveOrdWithExplicitTypeVariable { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.cmp(self)) + } +} + +#[derive(PartialOrd, PartialEq, Eq)] +struct DerivePartialOrd; + +impl std::cmp::Ord for DerivePartialOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } +} + +#[derive(PartialOrd, PartialEq, Eq)] +struct ImplUserOrd; + +trait Ord {} + +// We don't want to lint on user-defined traits called `Ord` +impl Ord for ImplUserOrd {} + +mod use_ord { + use std::cmp::{Ord, Ordering}; + + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} + +fn main() {} \ No newline at end of file diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr new file mode 100644 index 00000000000..30404ce4c54 --- /dev/null +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -0,0 +1 @@ +TODO \ No newline at end of file From 068acbd27b19a4a7be3a9d00954ecfad8a0e6553 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 22:04:46 -0600 Subject: [PATCH 0319/1110] initial implementation based on code for `derive_hash_xor_partial_eq` which is showing one error when there should be four --- clippy_lints/src/derive.rs | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 627475ee1d9..4f69c2d7af7 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -137,6 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let is_automatically_derived = is_automatically_derived(&*item.attrs); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); + check_ord_pord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); @@ -201,6 +202,60 @@ fn check_hash_peq<'tcx>( } } +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +fn check_ord_pord<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if_chain! { + if match_path(&trait_ref.path, &paths::ORD); + if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); + if let Some(def_id) = &trait_ref.trait_def_id(); + if !def_id.is_local(); + then { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(pord_trait_def_id, ty, |impl_id| { + let pord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + + if pord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.substs.type_at(1) == ty { + let mess = if pord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then( + cx, + DERIVE_ORD_XOR_PARTIAL_ORD, + span, + mess, + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + diag.span_note( + cx.tcx.hir().span(hir_id), + "`PartialOrd` implemented here" + ); + } + } + ); + } + }); + } + } +} + /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { if match_path(&trait_ref.path, &paths::CLONE_TRAIT) { From a8d6eda93049f0077c1515bec35fe0359ea43f96 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:04:04 -0600 Subject: [PATCH 0320/1110] use get_trait_def_id to check for Ord trait --- clippy_lints/src/derive.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 4f69c2d7af7..04395621e9e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -211,9 +211,10 @@ fn check_ord_pord<'tcx>( ord_is_automatically_derived: bool, ) { if_chain! { - if match_path(&trait_ref.path, &paths::ORD); + if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD); if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); if let Some(def_id) = &trait_ref.trait_def_id(); + if *def_id == ord_trait_def_id; if !def_id.is_local(); then { // Look for the PartialOrd implementations for `ty` From 6c3e4591b87e6c690b31166867484675dcb1e48c Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:04:25 -0600 Subject: [PATCH 0321/1110] update reference since we see the expected four errors --- tests/ui/derive_ord_xor_partial_ord.stderr | 72 +++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr index 30404ce4c54..66bc4d42ce8 100644 --- a/tests/ui/derive_ord_xor_partial_ord.stderr +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -1 +1,71 @@ -TODO \ No newline at end of file +error: you are deriving `Ord` but have implemented `PartialOrd` explicitly + --> $DIR/derive_ord_xor_partial_ord.rs:20:10 + | +LL | #[derive(Ord, PartialEq, Eq)] + | ^^^ + | + = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings` +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:23:1 + | +LL | / impl PartialOrd for DeriveOrd { +LL | | fn partial_cmp(&self, other: &Self) -> Option { +LL | | Some(other.cmp(self)) +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Ord` but have implemented `PartialOrd` explicitly + --> $DIR/derive_ord_xor_partial_ord.rs:29:10 + | +LL | #[derive(Ord, PartialEq, Eq)] + | ^^^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:32:1 + | +LL | / impl PartialOrd for DeriveOrdWithExplicitTypeVariable { +LL | | fn partial_cmp(&self, other: &Self) -> Option { +LL | | Some(other.cmp(self)) +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Ord` explicitly but have derived `PartialOrd` + --> $DIR/derive_ord_xor_partial_ord.rs:41:1 + | +LL | / impl std::cmp::Ord for DerivePartialOrd { +LL | | fn cmp(&self, other: &Self) -> Ordering { +LL | | Ordering::Less +LL | | } +LL | | } + | |_^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:38:10 + | +LL | #[derive(PartialOrd, PartialEq, Eq)] + | ^^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Ord` explicitly but have derived `PartialOrd` + --> $DIR/derive_ord_xor_partial_ord.rs:61:5 + | +LL | / impl Ord for DerivePartialOrdInUseOrd { +LL | | fn cmp(&self, other: &Self) -> Ordering { +LL | | Ordering::Less +LL | | } +LL | | } + | |_____^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:58:14 + | +LL | #[derive(PartialOrd, PartialEq, Eq)] + | ^^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + From 7dc974815ec8736f026dc10a070137e0d4601d52 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:06:36 -0600 Subject: [PATCH 0322/1110] remove is_local check since getting the def_id directly makes it unnecessary --- clippy_lints/src/derive.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 04395621e9e..ab001f7773e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -215,7 +215,6 @@ fn check_ord_pord<'tcx>( if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); if let Some(def_id) = &trait_ref.trait_def_id(); if *def_id == ord_trait_def_id; - if !def_id.is_local(); then { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(pord_trait_def_id, ty, |impl_id| { From 431924ccf69bc4d4f0597f12749e8b1bcb285710 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:15:36 -0600 Subject: [PATCH 0323/1110] add description for derive_ord_xor_partial_ord --- clippy_lints/src/derive.rs | 42 ++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index ab001f7773e..84566252abd 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -44,20 +44,50 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** + /// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` + /// explicitly or vice versa. /// - /// **Why is this bad?** + /// **Why is this bad?** The implementation of these traits must agree (for + /// example for use with `sort`) so it’s probably a bad idea to use a + /// default-generated `Ord` implementation with an explicitly defined + /// `PartialOrd`. In particular, the following must hold for any type + /// implementing `Ord`: + /// + /// ```text + /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() + /// ``` /// /// **Known problems:** None. /// /// **Example:** /// - /// ```rust - /// // example code where clippy issues a warning + /// ```rust,ignore + /// #[derive(Ord, PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// ... + /// } /// ``` /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning + /// ```rust,ignore + /// #[derive(PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// fn partial_cmp(&self, other: &Foo) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// + /// impl Ord for Foo { + /// ... + /// } + /// ``` + /// or, if you don't need a custom ordering: + /// ```rust,ignore + /// #[derive(Ord, PartialOrd, PartialEq, Eq)] + /// struct Foo; /// ``` pub DERIVE_ORD_XOR_PARTIAL_ORD, correctness, From 668b7474b47791c8c9af10130356b681b3bf3a84 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:30:00 -0600 Subject: [PATCH 0324/1110] run cargo dev fmt and fix overly long line --- clippy_lints/src/derive.rs | 10 ++++++++-- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 84566252abd..16a6f0c20e1 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, + span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -154,7 +155,12 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE]); +declare_lint_pass!(Derive => [ + EXPL_IMPL_CLONE_ON_COPY, + DERIVE_HASH_XOR_EQ, + DERIVE_ORD_XOR_PARTIAL_ORD, + UNSAFE_DERIVE_DESERIALIZE +]); impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 15f66b7a9c5..b82dc518a3b 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -65,4 +65,4 @@ mod use_ord { } } -fn main() {} \ No newline at end of file +fn main() {} From ca03f2b650a022d06df6c02c8947a74944815381 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Mon, 27 Jul 2020 00:21:11 -0600 Subject: [PATCH 0325/1110] s/pord/partial_ord/ to fix dogfood failure --- clippy_lints/src/derive.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 16a6f0c20e1..820ce85cff2 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -173,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let is_automatically_derived = is_automatically_derived(&*item.attrs); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); - check_ord_pord(cx, item.span, trait_ref, ty, is_automatically_derived); + check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); @@ -239,7 +239,7 @@ fn check_hash_peq<'tcx>( } /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. -fn check_ord_pord<'tcx>( +fn check_ord_partial_ord<'tcx>( cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, @@ -248,15 +248,15 @@ fn check_ord_pord<'tcx>( ) { if_chain! { if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD); - if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); + if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); if let Some(def_id) = &trait_ref.trait_def_id(); if *def_id == ord_trait_def_id; then { // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(pord_trait_def_id, ty, |impl_id| { - let pord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); - if pord_is_automatically_derived == ord_is_automatically_derived { + if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; } @@ -265,7 +265,7 @@ fn check_ord_pord<'tcx>( // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] if trait_ref.substs.type_at(1) == ty { - let mess = if pord_is_automatically_derived { + let mess = if partial_ord_is_automatically_derived { "you are implementing `Ord` explicitly but have derived `PartialOrd`" } else { "you are deriving `Ord` but have implemented `PartialOrd` explicitly" From 12a6eee045f30785a1eb7572a4cfea3c5cec8a4c Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Mon, 27 Jul 2020 00:22:39 -0600 Subject: [PATCH 0326/1110] fill in lint description for DERIVE_ORD_XOR_PARTIAL_ORD --- clippy_lints/src/derive.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 820ce85cff2..cdb748de0c0 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,6 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, - span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -92,7 +91,7 @@ declare_clippy_lint! { /// ``` pub DERIVE_ORD_XOR_PARTIAL_ORD, correctness, - "default lint description" + "deriving `Ord` but implementing `PartialOrd` explicitly" } declare_clippy_lint! { From 94b10a6e5ab003a03b6c7b60ffe5a3b366e0529a Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Mon, 27 Jul 2020 00:31:09 -0600 Subject: [PATCH 0327/1110] run cargo dev update_lints --- clippy_lints/src/derive.rs | 3 ++- src/lintlist/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index cdb748de0c0..08d8100a885 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, + span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 011504710e1..119908b3cc4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -363,7 +363,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "derive_ord_xor_partial_ord", group: "correctness", - desc: "default lint description", + desc: "deriving `Ord` but implementing `PartialOrd` explicitly", deprecation: None, module: "derive", }, From 3a9ccffed8c5329e3fda67c2e310086ba261e15f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 27 Jul 2020 22:27:54 +0900 Subject: [PATCH 0328/1110] `chmod` 644 `clippy_lints/src/utils/ast_utils.rs` --- clippy_lints/src/utils/ast_utils.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 clippy_lints/src/utils/ast_utils.rs diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs old mode 100755 new mode 100644 From 94c50bc8c913ef58eba0f4f10b682dcf6d6e0991 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 28 Jul 2020 16:23:47 +0200 Subject: [PATCH 0329/1110] Lint duplicate methods of trait bounds Fixes #5777 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/trait_bounds.rs | 94 ++++++++++++++++++++- src/lintlist/mod.rs | 7 ++ tests/ui/trait_duplication_in_bounds.rs | 31 +++++++ tests/ui/trait_duplication_in_bounds.stderr | 23 +++++ 6 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 tests/ui/trait_duplication_in_bounds.rs create mode 100644 tests/ui/trait_duplication_in_bounds.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..0ca4d88ed38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1723,6 +1723,7 @@ Released 2018-09-13 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..07ef087c2b0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -786,6 +786,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, &transmute::TRANSMUTE_BYTES_TO_STR, @@ -1174,6 +1175,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), + LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 0ef70311fb1..6bfdac37180 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_ use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{GenericBound, Generics, WherePredicate}; +use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds @@ -29,6 +30,35 @@ declare_clippy_lint! { "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } +declare_clippy_lint! { + /// **What it does:** Checks for cases where generics are being used and multiple + /// syntax specifications for trait bounds are used simultaneously. + /// + /// **Why is this bad?** Duplicate bounds makes the code + /// less readable than specifing them only once. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// fn func(arg: T) {} + /// ``` + /// or + /// /// + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + pub TRAIT_DUPLICATION_IN_BOUNDS, + pedantic, + "Check if the same trait bounds are specifed twice during a function declaration" +} + #[derive(Copy, Clone)] pub struct TraitBounds { max_trait_bounds: u64, @@ -41,10 +71,25 @@ impl TraitBounds { } } -impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + self.check_type_repetition(cx, gen); + check_trait_bound_duplication(cx, gen); + } +} + +fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { + if let GenericBound::Trait(t, _) = bound { + Some((t.trait_ref.path.res, t.span)) + } else { + None + } +} + +impl TraitBounds { + fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if in_macro(gen.span) { return; } @@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } } + +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + if in_macro(gen.span) { + return; + } + + let mut map = FxHashMap::default(); + for param in gen.params { + if let ParamName::Plain(ref ident) = param.name { + let res = param + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .collect::>(); + map.insert(*ident, res); + } + } + + for predicate in gen.where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !in_macro(bound_predicate.span); + if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind; + if let QPath::Resolved(_, Path { ref segments, .. }) = path; + if let Some(segment) = segments.first(); + if let Some(trait_resolutions_direct) = map.get(&segment.ident); + then { + for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { + if let Some((_, span_direct)) = trait_resolutions_direct + .iter() + .find(|(res_direct, _)| *res_direct == res_where) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + *span_direct, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); + } + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..9fb3dfc96ec 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "trait_duplication_in_bounds", + group: "pedantic", + desc: "Check if the same trait bounds are specifed twice during a function declaration", + deprecation: None, + module: "trait_bounds", + }, Lint { name: "transmute_bytes_to_str", group: "complexity", diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs new file mode 100644 index 00000000000..cb2b0054e35 --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -0,0 +1,31 @@ +#![deny(clippy::trait_duplication_in_bounds)] + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +fn bad_foo(arg0: T, arg1: Z) +where + T: Clone, + T: Default, +{ + unimplemented!(); +} + +fn good_bar(arg: T) { + unimplemented!(); +} + +fn good_foo(arg: T) +where + T: Clone + Default, +{ + unimplemented!(); +} + +fn good_foobar(arg: T) +where + T: Clone, +{ + unimplemented!(); +} + +fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr new file mode 100644 index 00000000000..027e1c75204 --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -0,0 +1,23 @@ +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:15 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/trait_duplication_in_bounds.rs:1:9 + | +LL | #![deny(clippy::trait_duplication_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:23 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 2 previous errors + From 2b7fde6a4b18ee837342f5b50a4c4941e919177f Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 29 Jul 2020 16:10:15 +0200 Subject: [PATCH 0330/1110] typo fix --- clippy_lints/src/trait_bounds.rs | 2 +- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 6bfdac37180..10811374875 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { /// ``` pub TRAIT_DUPLICATION_IN_BOUNDS, pedantic, - "Check if the same trait bounds are specifed twice during a function declaration" + "Check if the same trait bounds are specified twice during a function declaration" } #[derive(Copy, Clone)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 9fb3dfc96ec..197eab759f1 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2169,7 +2169,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "trait_duplication_in_bounds", group: "pedantic", - desc: "Check if the same trait bounds are specifed twice during a function declaration", + desc: "Check if the same trait bounds are specified twice during a function declaration", deprecation: None, module: "trait_bounds", }, From a427c99f3d2a0b2c55d19af73bcad81f1dc761ab Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 25 Jul 2020 20:04:59 +0300 Subject: [PATCH 0331/1110] Handle mapping to Option in `map_flatten` lint --- clippy_lints/src/methods/mod.rs | 26 ++++++++++++++++++++++---- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/map_flatten.stderr | 16 +++++++++++----- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9edcdd979ff..3f62a3cab1c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2569,17 +2569,35 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; + let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); + let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind { + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + } else { + false + }; + + let method_to_use = if is_map_to_option { + // `(...).map(...)` has type `impl Iterator> + "filter_map" + } else { + // `(...).map(...)` has type `impl Iterator> + "flat_map" + }; + let msg = &format!( + "called `map(..).flatten()` on an `Iterator`. \ + This is more succinctly expressed by calling `.{}(..)`", + method_to_use + ); let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); + let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, expr.span, msg, - "try using `flat_map` instead", + &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, ); diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 4171d80f48a..684a28aebcb 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 16a0fd090ad..05789ee5232 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 00bc41c15e9..d2d15362a6c 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,16 +1,22 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)` --> $DIR/map_flatten.rs:8:21 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))` | = note: `-D clippy::map-flatten` implied by `-D warnings` +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` + --> $DIR/map_flatten.rs:9:21 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:9:24 + --> $DIR/map_flatten.rs:10:24 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors From d4ba561aafb501972f581c1f8e6d1885959f9306 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Thu, 30 Jul 2020 22:20:31 +0300 Subject: [PATCH 0332/1110] Review fixes --- clippy_lints/src/methods/mod.rs | 36 +++++++++++++---------------- tests/ui/map_flatten.fixed | 13 +++++++++++ tests/ui/map_flatten.rs | 13 +++++++++++ tests/ui/map_flatten.stderr | 40 ++++++++++++++++++++++++--------- 4 files changed, 71 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3f62a3cab1c..9217324b18c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2570,11 +2570,16 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); - let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind { - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) - } else { - false + let is_map_to_option = match map_closure_ty.kind { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + }, + _ => false, }; let method_to_use = if is_map_to_option { @@ -2584,19 +2589,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // `(...).map(...)` has type `impl Iterator> "flat_map" }; - let msg = &format!( - "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.{}(..)`", - method_to_use - ); - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet); + let hint = format!(".{0}({1})", method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Iterator`", &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, @@ -2605,16 +2604,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Option if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { - let msg = "called `map(..).flatten()` on an `Option`. \ - This is more succinctly expressed by calling `.and_then(..)`"; - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.and_then({1})", self_snippet, func_snippet); + let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Option`", "try using `and_then` instead", hint, Applicability::MachineApplicable, diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 684a28aebcb..a5fdf7df613 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,7 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 05789ee5232..abbc4e16e56 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,7 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = 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(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index d2d15362a6c..b6479cd69ea 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,22 +1,40 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)` - --> $DIR/map_flatten.rs:8:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:14:46 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` | = note: `-D clippy::map-flatten` implied by `-D warnings` -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` - --> $DIR/map_flatten.rs:9:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:15:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:16:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:17:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:20:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` -error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:10:24 +error: called `map(..).flatten()` on an `Option` + --> $DIR/map_flatten.rs:23:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors From cb00cdf0d77e6a21cd64558f1e373e696f31d301 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 2 Aug 2020 11:25:03 +0200 Subject: [PATCH 0333/1110] Remove old Symbol reexport I couldn't really tell what it was meant to improve. It seems more clear without the renaming to `Name`? --- clippy_lints/src/attrs.rs | 3 +-- clippy_lints/src/lib.rs | 4 ---- clippy_lints/src/lifetimes.rs | 7 +++---- clippy_lints/src/loops.rs | 17 ++++++++--------- clippy_lints/src/shadow.rs | 18 +++++++++--------- clippy_lints/src/utils/mod.rs | 15 +++++++-------- 6 files changed, 28 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c29432bf933..ed02763397a 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,6 +1,5 @@ //! checks for attributes -use crate::reexport::Name; use crate::utils::{ first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, without_block_comments, @@ -514,7 +513,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: & } } -fn check_attrs(cx: &LateContext<'_>, span: Span, name: Name, attrs: &[Attribute]) { +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { if span.from_expansion() { return; } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7a4ca3902b3..b336de37c61 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -321,10 +321,6 @@ mod zero_div_zero; pub use crate::utils::conf::Conf; -mod reexport { - pub use rustc_span::Symbol as Name; -} - /// Register all pre expansion lints /// /// Pre-expansion lints run before any macro expansion has happened. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 168f9f953e4..1b3639a02a0 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,9 +13,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::kw; +use rustc_span::symbol::{Symbol, kw}; -use crate::reexport::Name; use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { @@ -113,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { enum RefLt { Unnamed, Static, - Named(Name), + Named(Symbol), } fn check_fn_inner<'tcx>( @@ -456,7 +455,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl } struct LifetimeChecker { - map: FxHashMap, + map: FxHashMap, } impl<'tcx> Visitor<'tcx> for LifetimeChecker { diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 396bb659109..d0d6c2e04a0 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,5 +1,4 @@ use crate::consts::constant; -use crate::reexport::Name; use crate::utils::paths; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ @@ -1184,7 +1183,7 @@ fn check_for_loop_range<'tcx>( } } -fn is_len_call(expr: &Expr<'_>, var: Name) -> bool { +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { if_chain! { if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind; if len_args.len() == 1; @@ -1632,15 +1631,15 @@ struct VarVisitor<'a, 'tcx> { /// var name to look for as index var: HirId, /// indexed variables that are used mutably - indexed_mut: FxHashSet, + indexed_mut: FxHashSet, /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global - indexed_indirectly: FxHashMap>, + indexed_indirectly: FxHashMap>, /// subset of `indexed` of vars that are indexed directly: `v[i]` /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` - indexed_directly: FxHashMap, Ty<'tcx>)>, + indexed_directly: FxHashMap, Ty<'tcx>)>, /// Any names that are used outside an index operation. /// Used to detect things like `&mut vec` used together with `vec[i]` - referenced: FxHashSet, + referenced: FxHashSet, /// has the loop variable been used in expressions other than the index of /// an index op? nonindex: bool, @@ -1996,7 +1995,7 @@ struct InitializeVisitor<'a, 'tcx> { end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, state: VarState, - name: Option, + name: Option, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2159,7 +2158,7 @@ use self::Nesting::{LookFurther, RuledOut, Unknown}; struct LoopNestVisitor { hir_id: HirId, - iterator: Name, + iterator: Symbol, nesting: Nesting, } @@ -2210,7 +2209,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { } } -fn path_name(e: &Expr<'_>) -> Option { +fn path_name(e: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind { let segments = &path.segments; if segments.len() == 1 { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index fab13c8c124..97bd52e517c 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,4 +1,3 @@ -use crate::reexport::Name; use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -10,6 +9,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; declare_clippy_lint! { /// **What it does:** Checks for bindings that shadow other bindings already in @@ -123,7 +123,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo check_expr(cx, &body.value, &mut bindings); } -fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) { let len = bindings.len(); for stmt in block.stmts { match stmt.kind { @@ -138,7 +138,7 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: & bindings.truncate(len); } -fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) { if in_external_macro(cx.sess(), local.span) { return; } @@ -173,7 +173,7 @@ fn check_pat<'tcx>( pat: &'tcx Pat<'_>, init: Option<&'tcx Expr<'_>>, span: Span, - bindings: &mut Vec<(Name, Span)>, + bindings: &mut Vec<(Symbol, Span)>, ) { // TODO: match more stuff / destructuring match pat.kind { @@ -254,7 +254,7 @@ fn check_pat<'tcx>( fn lint_shadow<'tcx>( cx: &LateContext<'tcx>, - name: Name, + name: Symbol, span: Span, pattern_span: Span, init: Option<&'tcx Expr<'_>>, @@ -315,7 +315,7 @@ fn lint_shadow<'tcx>( } } -fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) { if in_external_macro(cx.sess(), expr.span) { return; } @@ -351,7 +351,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut } } -fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) { match ty.kind { TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), TyKind::Array(ref fty, ref anon_const) => { @@ -371,7 +371,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( } } -fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { +fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner), ExprKind::Block(ref block, _) => { @@ -383,6 +383,6 @@ fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { } } -fn path_eq_name(name: Name, path: &Path<'_>) -> bool { +fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool { !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str() } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4b7a1c2b537..39317ecc82e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -52,7 +52,6 @@ use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; use crate::consts::{constant, Constant}; -use crate::reexport::Name; /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). @@ -150,7 +149,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) } /// Checks if an expression references a variable of the given name. -pub fn match_var(expr: &Expr<'_>, var: Name) -> bool { +pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { if let [p] = path.segments { return p.ident.name == var; @@ -422,7 +421,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool { } /// Gets the name of the item the expression is in, if available. -pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); match cx.tcx.hir().find(parent_id) { Some( @@ -435,7 +434,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } /// Gets the name of a `Pat`, if any. -pub fn get_pat_name(pat: &Pat<'_>) -> Option { +pub fn get_pat_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ref spname, _) => Some(spname.name), PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), @@ -445,14 +444,14 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option { } struct ContainsName { - name: Name, + name: Symbol, result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName { type Map = Map<'tcx>; - fn visit_name(&mut self, _: Span, name: Name) { + fn visit_name(&mut self, _: Span, name: Symbol) { if self.name == name { self.result = true; } @@ -463,7 +462,7 @@ impl<'tcx> Visitor<'tcx> for ContainsName { } /// Checks if an `Expr` contains a certain name. -pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool { +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { let mut cn = ContainsName { name, result: false }; cn.visit_expr(expr); cn.result @@ -1029,7 +1028,7 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow } -pub fn get_arg_name(pat: &Pat<'_>) -> Option { +pub fn get_arg_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ident, None) => Some(ident.name), PatKind::Ref(ref subpat, _) => get_arg_name(subpat), From bb6e857980748b000ba3b88d125c6b29aced0693 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 2 Aug 2020 14:22:54 +0200 Subject: [PATCH 0334/1110] fmt --- clippy_lints/src/lifetimes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 1b3639a02a0..4df6827d77f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,7 +13,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::{Symbol, kw}; +use rustc_span::symbol::{kw, Symbol}; use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; From e336fe80d2f991a170b98190683039035b53c6ba Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 3 Aug 2020 00:36:28 +0200 Subject: [PATCH 0335/1110] manual_async_fn: take input lifetimes into account The anonymous future returned from an `async fn` captures all input lifetimes. This was not being taken into account. See https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#lifetime-capture-in-the-anonymous-future --- clippy_lints/src/manual_async_fn.rs | 63 ++++++++++++++++++++++++----- tests/ui/await_holding_lock.rs | 1 + tests/ui/await_holding_lock.stderr | 4 +- tests/ui/manual_async_fn.fixed | 40 ++++++++++++++++-- tests/ui/manual_async_fn.rs | 48 ++++++++++++++++++---- tests/ui/manual_async_fn.stderr | 30 +++++++------- 6 files changed, 146 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index c19fb148cda..864d1ea87f5 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -4,8 +4,8 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, - ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, + IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -27,8 +27,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// use std::future::Future; - /// /// async fn foo() -> i32 { 42 } /// ``` pub MANUAL_ASYNC_FN, @@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let IsAsync::NotAsync = header.asyncness; // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; - if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); if let Some(output) = future_output_ty(trait_ref); + if captures_all_lifetimes(decl.inputs, &output_lifetimes); // Check that the body of the function consists of one async block if let ExprKind::Block(block, _) = body.value.kind; if block.stmts.is_empty(); @@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { } } -fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { +fn future_trait_ref<'tcx>( + cx: &LateContext<'tcx>, + ty: &'tcx Ty<'tcx>, +) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { if_chain! { - if let TyKind::OpaqueDef(item_id, _) = ty.kind; + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind; let item = cx.tcx.hir().item(item_id.id); if let ItemKind::OpaqueTy(opaque) = &item.kind; - if opaque.bounds.len() == 1; - if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); + if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { + if let GenericBound::Trait(poly, _) = bound { + Some(&poly.trait_ref) + } else { + None + } + }); + if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { - return Some(&poly.trait_ref); + let output_lifetimes = bounds + .iter() + .filter_map(|bound| { + if let GenericArg::Lifetime(lt) = bound { + Some(lt.name) + } else { + None + } + }) + .collect(); + + return Some((trait_ref, output_lifetimes)); } } @@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t None } +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool { + let input_lifetimes: Vec = inputs + .iter() + .filter_map(|ty| { + if let TyKind::Rptr(lt, _) = ty.kind { + Some(lt.name) + } else { + None + } + }) + .collect(); + + // The lint should trigger in one of these cases: + // - There are no input lifetimes + // - There's only one output lifetime bound using `+ '_` + // - All input lifetimes are explicitly bound to the output + input_lifetimes.is_empty() + || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || input_lifetimes + .iter() + .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) +} + fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs index 5c1fdd83efb..0458950edee 100644 --- a/tests/ui/await_holding_lock.rs +++ b/tests/ui/await_holding_lock.rs @@ -47,6 +47,7 @@ async fn not_good(x: &Mutex) -> u32 { first + second + third } +#[allow(clippy::manual_async_fn)] fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 8c47cb37d8c..21bf49d16f0 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -46,13 +46,13 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:52:13 + --> $DIR/await_holding_lock.rs:53:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:52:9 + --> $DIR/await_holding_lock.rs:53:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 27222cc0869..4f551690c43 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -43,10 +43,6 @@ impl S { 42 } - async fn meth_fut(&self) -> i32 { 42 } - - async fn empty_fut(&self) {} - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -64,4 +60,40 @@ impl S { } } +// Tests related to lifetime capture + +async fn elided(_: &i32) -> i32 { 42 } + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index 6a0f1b26c88..6ed60309947 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -51,14 +51,6 @@ impl S { } } - fn meth_fut(&self) -> impl Future { - async { 42 } - } - - fn empty_fut(&self) -> impl Future { - async {} - } - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -76,4 +68,44 @@ impl S { } } +// Tests related to lifetime capture + +fn elided(_: &i32) -> impl Future + '_ { + async { 42 } +} + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + async { 42 } +} + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index a1904c904d0..ccd82867427 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -65,34 +65,34 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:54:5 + --> $DIR/manual_async_fn.rs:73:1 | -LL | fn meth_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn meth_fut(&self) -> i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn elided(_: &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn meth_fut(&self) -> impl Future { 42 } - | ^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { 42 } + | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:58:5 + --> $DIR/manual_async_fn.rs:82:1 | -LL | fn empty_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type +help: make the function `async` and return the output of the future directly | -LL | async fn empty_fut(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn empty_fut(&self) -> impl Future {} - | ^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } + | ^^^^^^ error: aborting due to 6 previous errors From 05bb6e6bdb1894de5803f729339a631a9222499f Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 20 Jul 2020 08:58:55 -0700 Subject: [PATCH 0336/1110] Create test for wanted behavior --- tests/ui/needless_collect.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 7ee603afeb0..1577e7a46ed 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -8,6 +8,9 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[allow(unused_variables, clippy::iter_cloned_collect)] fn main() { let sample = [1; 5]; + let indirect_with_into_iter = sample.iter().collect::>(); + let indirect_with_iter = sample.iter().collect::>();; + let indirect_negative = sample.iter().collect::>();; let len = sample.iter().collect::>().len(); if sample.iter().collect::>().is_empty() { // Empty @@ -18,4 +21,8 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + indirect_with_into_iter.into_iter().map(|x| (x, x+1)).collect::>(); + indirect_with_iter.iter().map(|x| (x, x+1)).collect::>(); + indirect_negative.iter().map(|x| (x, x+1)).collect::>(); + indirect_negative.iter().map(|x| (x, x+1)).collect::>(); } From 3ee61373fe056efb46b6b1b243b31cec0d7e6099 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 22 Jul 2020 22:46:23 -0700 Subject: [PATCH 0337/1110] Write the lint and write tests --- clippy_lints/src/loops.rs | 107 +++++++++++++++++++++++++++++-- tests/ui/needless_collect.fixed | 11 ++++ tests/ui/needless_collect.rs | 18 ++++-- tests/ui/needless_collect.stderr | 17 ++++- 4 files changed, 137 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7e3876ff49b..231c440463d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,14 +1,15 @@ use crate::consts::constant; use crate::reexport::Name; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var, - multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, SpanlessEq, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_path, + match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -17,7 +18,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; use rustc_hir::{ def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, - LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, + Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -27,7 +28,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -2358,6 +2359,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + // Check for direct, immediate usage if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2423,6 +2425,99 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } } + // Check for collecting it and then turning it back into an iterator later + if let ExprKind::Block(ref block, _) = expr.kind { + for ref stmt in block.stmts { + if_chain! { + // TODO also work for assignments to an existing variable + if let StmtKind::Local( + Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + init: Some(ref init_expr), .. } + ) = stmt.kind; + if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR); + if let Some(ref generic_args) = method_name.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + if let ty = cx.typeck_results().node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || + match_type(cx, ty, &paths::LINKED_LIST); + if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); + if iter_calls.len() == 1; + then { + // Suggest replacing iter_call with iter_replacement, and removing stmt + span_lint_and_then( + cx, + NEEDLESS_COLLECT, + stmt.span, + NEEDLESS_COLLECT_MSG, + |diag| { + let iter_replacement = Sugg::hir(cx, iter_source, "..").to_string(); + diag.multipart_suggestion( + "Use the original Iterator instead of collecting it and then producing a new one", + vec![ + (stmt.span, String::new()), + (iter_calls[0].span, iter_replacement) + ], + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } +} + +struct IntoIterVisitor<'tcx> { + iters: Vec<&'tcx Expr<'tcx>>, + seen_other: bool, + target: String, +} +impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::MethodCall( + method_name, + _, + &[Expr { + kind: ExprKind::Path(QPath::Resolved(_, ref path)), + .. + }], + _, + ) if match_path(path, &[&self.target]) => { + // TODO Check what method is being called, if it's called on target, and act + // accordingly + if method_name.ident.name == sym!(into_iter) { + self.iters.push(expr); + } else { + self.seen_other = true; + } + }, + _ => walk_expr(self, expr), + } + } + + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Detect the occurences of calls to `iter` or `into_iter` for the +/// given identifier +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option>> { + let mut visitor = IntoIterVisitor { + iters: Vec::new(), + target: identifier.name.to_ident_string(), + seen_other: false, + }; + visitor.visit_block(block); + if visitor.seen_other { + None + } else { + Some(visitor.iters) + } } fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index be37dc16b9a..60a3e206283 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -18,4 +18,15 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + let indirect_positive = sample.iter().collect::>(); + indirect_positive + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .iter() + .map(|x| (*x, *x + 1)) + .collect::>(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 1577e7a46ed..33a1ea36095 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -8,9 +8,6 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[allow(unused_variables, clippy::iter_cloned_collect)] fn main() { let sample = [1; 5]; - let indirect_with_into_iter = sample.iter().collect::>(); - let indirect_with_iter = sample.iter().collect::>();; - let indirect_negative = sample.iter().collect::>();; let len = sample.iter().collect::>().len(); if sample.iter().collect::>().is_empty() { // Empty @@ -21,8 +18,15 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - indirect_with_into_iter.into_iter().map(|x| (x, x+1)).collect::>(); - indirect_with_iter.iter().map(|x| (x, x+1)).collect::>(); - indirect_negative.iter().map(|x| (x, x+1)).collect::>(); - indirect_negative.iter().map(|x| (x, x+1)).collect::>(); + let indirect_positive = sample.iter().collect::>(); + indirect_positive + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .iter() + .map(|x| (*x, *x + 1)) + .collect::>(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 9113aad90dd..bb67bfa83e9 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,10 +1,21 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:21:5 + | +LL | let indirect_positive = sample.iter().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-collect` implied by `-D warnings` +help: Use the original Iterator instead of collecting it and then producing a new one + | +LL | +LL | sample.iter() + | + error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` - | - = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:12:15 @@ -24,5 +35,5 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From 3657c92ac978f69667b9c8bb46e51bc602b3d7ee Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:14:10 -0700 Subject: [PATCH 0338/1110] Check for other things which can be used indirectly --- clippy_lints/src/loops.rs | 94 ++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 231c440463d..11a9b1e531c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2429,7 +2429,6 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if let ExprKind::Block(ref block, _) = expr.kind { for ref stmt in block.stmts { if_chain! { - // TODO also work for assignments to an existing variable if let StmtKind::Local( Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } @@ -2446,21 +2445,22 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if iter_calls.len() == 1; then { // Suggest replacing iter_call with iter_replacement, and removing stmt + let iter_call = &iter_calls[0]; span_lint_and_then( cx, NEEDLESS_COLLECT, - stmt.span, + stmt.span.until(iter_call.span), NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = Sugg::hir(cx, iter_source, "..").to_string(); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( - "Use the original Iterator instead of collecting it and then producing a new one", + iter_call.get_suggestion_text(), vec![ (stmt.span, String::new()), - (iter_calls[0].span, iter_replacement) + (iter_call.span, iter_replacement) ], - Applicability::MaybeIncorrect, - ); + Applicability::MachineApplicable,// MaybeIncorrect, + ).emit(); }, ); } @@ -2469,32 +2469,62 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } -struct IntoIterVisitor<'tcx> { - iters: Vec<&'tcx Expr<'tcx>>, +struct IterFunction { + func: IterFunctionKind, + span: Span, +} +impl IterFunction { + fn get_iter_method(&self, cx: &LateContext<'_>) -> String { + match &self.func { + IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::Len => String::from(".count()"), + IterFunctionKind::IsEmpty => String::from(".next().is_none()"), + IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")), + } + } + fn get_suggestion_text(&self) -> &'static str { + match &self.func { + IterFunctionKind::IntoIter => "Use the original Iterator instead of collecting it and then producing a new one", + IterFunctionKind::Len => "Take the original Iterator's count instead of collecting it and finding the length", + IterFunctionKind::IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty", + IterFunctionKind::Contains(_) => "Check if the original Iterator contains an element instead of collecting then checking", + } + } +} +enum IterFunctionKind { + IntoIter, + Len, + IsEmpty, + Contains(Span), +} + +struct IterFunctionVisitor { + uses: Vec, seen_other: bool, target: String, } -impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match &expr.kind { - ExprKind::MethodCall( - method_name, - _, - &[Expr { - kind: ExprKind::Path(QPath::Resolved(_, ref path)), - .. - }], - _, - ) if match_path(path, &[&self.target]) => { - // TODO Check what method is being called, if it's called on target, and act - // accordingly - if method_name.ident.name == sym!(into_iter) { - self.iters.push(expr); - } else { - self.seen_other = true; + if_chain! { + if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; + if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); + if match_path(path, &[&self.target]); + then { + let into_iter = sym!(into_iter); + let len = sym!(len); + let is_empty = sym!(is_empty); + let contains = sym!(contains); + match method_name.ident.name { + name if name == into_iter => self.uses.push(IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }), + name if name == len => self.uses.push(IterFunction { func: IterFunctionKind::Len, span: expr.span }), + name if name == is_empty => self.uses.push(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }), + name if name == contains => self.uses.push(IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }), + _ => self.seen_other = true, } - }, - _ => walk_expr(self, expr), + } + else { + walk_expr(self, expr); + } } } @@ -2506,9 +2536,9 @@ impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { /// Detect the occurences of calls to `iter` or `into_iter` for the /// given identifier -fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option>> { - let mut visitor = IntoIterVisitor { - iters: Vec::new(), +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { + let mut visitor = IterFunctionVisitor { + uses: Vec::new(), target: identifier.name.to_ident_string(), seen_other: false, }; @@ -2516,7 +2546,7 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) if visitor.seen_other { None } else { - Some(visitor.iters) + Some(visitor.uses) } } From c86f4109fdd83fef1ea69c0f3c878ace0aa7c56f Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:15:16 -0700 Subject: [PATCH 0339/1110] Split indirect collects into their own test case --- tests/ui/needless_collect.fixed | 11 ----- tests/ui/needless_collect.rs | 11 ----- tests/ui/needless_collect.stderr | 17 ++----- tests/ui/needless_collect_indirect.fixed | 26 +++++++++++ tests/ui/needless_collect_indirect.rs | 26 +++++++++++ tests/ui/needless_collect_indirect.stderr | 55 +++++++++++++++++++++++ 6 files changed, 110 insertions(+), 36 deletions(-) create mode 100644 tests/ui/needless_collect_indirect.fixed create mode 100644 tests/ui/needless_collect_indirect.rs create mode 100644 tests/ui/needless_collect_indirect.stderr diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 60a3e206283..be37dc16b9a 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -18,15 +18,4 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - let indirect_positive = sample.iter().collect::>(); - indirect_positive - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .iter() - .map(|x| (*x, *x + 1)) - .collect::>(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 33a1ea36095..7ee603afeb0 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -18,15 +18,4 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - let indirect_positive = sample.iter().collect::>(); - indirect_positive - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .iter() - .map(|x| (*x, *x + 1)) - .collect::>(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index bb67bfa83e9..9113aad90dd 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,21 +1,10 @@ -error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:21:5 - | -LL | let indirect_positive = sample.iter().collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::needless-collect` implied by `-D warnings` -help: Use the original Iterator instead of collecting it and then producing a new one - | -LL | -LL | sample.iter() - | - error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:12:15 @@ -35,5 +24,5 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed new file mode 100644 index 00000000000..136af42a9fe --- /dev/null +++ b/tests/ui/needless_collect_indirect.fixed @@ -0,0 +1,26 @@ +// run-rustfix + +#[allow(unused)] + +use std::collections::{HashMap, VecDeque}; + +fn main() { + let sample = [1; 5]; + let indirect_iter = sample.iter().collect::>(); + indirect_iter + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_len = sample.iter().collect::>(); + indirect_len.len(); + let indirect_empty = sample.iter().collect::>(); + indirect_empty.is_empty(); + let indirect_contains = sample.iter().collect::>(); + indirect_contains.contains(&&5); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .into_iter() + .map(|x| (*x, *x + 1)) + .collect::>(); +} diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs new file mode 100644 index 00000000000..136af42a9fe --- /dev/null +++ b/tests/ui/needless_collect_indirect.rs @@ -0,0 +1,26 @@ +// run-rustfix + +#[allow(unused)] + +use std::collections::{HashMap, VecDeque}; + +fn main() { + let sample = [1; 5]; + let indirect_iter = sample.iter().collect::>(); + indirect_iter + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_len = sample.iter().collect::>(); + indirect_len.len(); + let indirect_empty = sample.iter().collect::>(); + indirect_empty.is_empty(); + let indirect_contains = sample.iter().collect::>(); + indirect_contains.contains(&&5); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .into_iter() + .map(|x| (*x, *x + 1)) + .collect::>(); +} diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr new file mode 100644 index 00000000000..5058c171ac2 --- /dev/null +++ b/tests/ui/needless_collect_indirect.stderr @@ -0,0 +1,55 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:9:5 + | +LL | / let indirect_iter = sample.iter().collect::>(); +LL | | indirect_iter + | |____^ + | + = note: `-D clippy::needless-collect` implied by `-D warnings` +help: Use the original Iterator instead of collecting it and then producing a new one + | +LL | +LL | sample.iter() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:14:5 + | +LL | / let indirect_len = sample.iter().collect::>(); +LL | | indirect_len.len(); + | |____^ + | +help: Take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:16:5 + | +LL | / let indirect_empty = sample.iter().collect::>(); +LL | | indirect_empty.is_empty(); + | |____^ + | +help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty + | +LL | +LL | sample.iter().next().is_none(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:18:5 + | +LL | / let indirect_contains = sample.iter().collect::>(); +LL | | indirect_contains.contains(&&5); + | |____^ + | +help: Check if the original Iterator contains an element instead of collecting then checking + | +LL | +LL | sample.iter().any(|x| x == &&5); + | + +error: aborting due to 4 previous errors + From a84948329459e650af4eb2f8cff8f55282316ea5 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:44:44 -0700 Subject: [PATCH 0340/1110] Fix formatting and dogfood fallout --- clippy_lints/src/loops.rs | 43 +++++++++++++++++------ tests/ui/needless_collect_indirect.fixed | 6 +--- tests/ui/needless_collect_indirect.rs | 6 +--- tests/ui/needless_collect_indirect.stderr | 12 +++---- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 11a9b1e531c..2181304f006 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2359,7 +2359,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - // Check for direct, immediate usage + check_needless_collect_direct_usage(expr, cx); + check_needless_collect_indirect_usage(expr, cx); +} +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2425,7 +2428,9 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } } - // Check for collecting it and then turning it back into an iterator later +} + +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if let ExprKind::Block(ref block, _) = expr.kind { for ref stmt in block.stmts { if_chain! { @@ -2484,10 +2489,18 @@ impl IterFunction { } fn get_suggestion_text(&self) -> &'static str { match &self.func { - IterFunctionKind::IntoIter => "Use the original Iterator instead of collecting it and then producing a new one", - IterFunctionKind::Len => "Take the original Iterator's count instead of collecting it and finding the length", - IterFunctionKind::IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty", - IterFunctionKind::Contains(_) => "Check if the original Iterator contains an element instead of collecting then checking", + IterFunctionKind::IntoIter => { + "Use the original Iterator instead of collecting it and then producing a new one" + }, + IterFunctionKind::Len => { + "Take the original Iterator's count instead of collecting it and finding the length" + }, + IterFunctionKind::IsEmpty => { + "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" + }, + IterFunctionKind::Contains(_) => { + "Check if the original Iterator contains an element instead of collecting then checking" + }, } } } @@ -2505,6 +2518,8 @@ struct IterFunctionVisitor { } impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + // TODO Check if the target identifier is being used in something other + // than a function call if_chain! { if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); @@ -2515,10 +2530,18 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { let is_empty = sym!(is_empty); let contains = sym!(contains); match method_name.ident.name { - name if name == into_iter => self.uses.push(IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }), - name if name == len => self.uses.push(IterFunction { func: IterFunctionKind::Len, span: expr.span }), - name if name == is_empty => self.uses.push(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }), - name if name == contains => self.uses.push(IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }), + name if name == into_iter => self.uses.push( + IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } + ), + name if name == len => self.uses.push( + IterFunction { func: IterFunctionKind::Len, span: expr.span } + ), + name if name == is_empty => self.uses.push( + IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span } + ), + name if name == contains => self.uses.push( + IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span } + ), _ => self.seen_other = true, } } diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed index 136af42a9fe..163eaf965dd 100644 --- a/tests/ui/needless_collect_indirect.fixed +++ b/tests/ui/needless_collect_indirect.fixed @@ -1,16 +1,12 @@ // run-rustfix #[allow(unused)] - use std::collections::{HashMap, VecDeque}; fn main() { let sample = [1; 5]; let indirect_iter = sample.iter().collect::>(); - indirect_iter - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); let indirect_len = sample.iter().collect::>(); indirect_len.len(); let indirect_empty = sample.iter().collect::>(); diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 136af42a9fe..163eaf965dd 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,16 +1,12 @@ // run-rustfix #[allow(unused)] - use std::collections::{HashMap, VecDeque}; fn main() { let sample = [1; 5]; let indirect_iter = sample.iter().collect::>(); - indirect_iter - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); let indirect_len = sample.iter().collect::>(); indirect_len.len(); let indirect_empty = sample.iter().collect::>(); diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 5058c171ac2..700c73b0b22 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,19 +1,19 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:5 + --> $DIR/needless_collect_indirect.rs:8:5 | LL | / let indirect_iter = sample.iter().collect::>(); -LL | | indirect_iter +LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); | |____^ | = note: `-D clippy::needless-collect` implied by `-D warnings` help: Use the original Iterator instead of collecting it and then producing a new one | LL | -LL | sample.iter() +LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:14:5 + --> $DIR/needless_collect_indirect.rs:10:5 | LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); @@ -26,7 +26,7 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:16:5 + --> $DIR/needless_collect_indirect.rs:12:5 | LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); @@ -39,7 +39,7 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:18:5 + --> $DIR/needless_collect_indirect.rs:14:5 | LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); From bb2c14e92b5a0eafe9c9f43f3a02e33b544b3f91 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 10:07:51 -0700 Subject: [PATCH 0341/1110] Fix a bug causing it to be too trigger-happy --- clippy_lints/src/loops.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2181304f006..c931c212735 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,10 +5,9 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_path, - match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, - snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - SpanlessEq, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -2514,16 +2513,16 @@ enum IterFunctionKind { struct IterFunctionVisitor { uses: Vec, seen_other: bool, - target: String, + target: Ident, } impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - // TODO Check if the target identifier is being used in something other - // than a function call + // Check function calls on our collection if_chain! { if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); - if match_path(path, &[&self.target]); + if let &[name] = &path.segments; + if name.ident == self.target; then { let into_iter = sym!(into_iter); let len = sym!(len); @@ -2544,8 +2543,17 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { ), _ => self.seen_other = true, } + return } - else { + } + // Check if the collection is used for anything else + if_chain! { + if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr; + if let &[name] = &path.segments; + if name.ident == self.target; + then { + self.seen_other = true; + } else { walk_expr(self, expr); } } @@ -2562,7 +2570,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { let mut visitor = IterFunctionVisitor { uses: Vec::new(), - target: identifier.name.to_ident_string(), + target: identifier, seen_other: false, }; visitor.visit_block(block); From 5e10b039a3f215b48a54ce7dd38f95115f92e55a Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 2 Aug 2020 21:46:18 -0700 Subject: [PATCH 0342/1110] Implement review suggestions --- clippy_lints/src/loops.rs | 7 +++---- tests/ui/needless_collect_indirect.fixed | 22 ---------------------- tests/ui/needless_collect_indirect.rs | 3 --- tests/ui/needless_collect_indirect.stderr | 8 ++++---- 4 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 tests/ui/needless_collect_indirect.fixed diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c931c212735..e9ce804320f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -2442,7 +2442,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(ref generic_args) = method_name.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); @@ -2524,12 +2524,11 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { if let &[name] = &path.segments; if name.ident == self.target; then { - let into_iter = sym!(into_iter); let len = sym!(len); let is_empty = sym!(is_empty); let contains = sym!(contains); match method_name.ident.name { - name if name == into_iter => self.uses.push( + sym::into_iter => self.uses.push( IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } ), name if name == len => self.uses.push( diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed deleted file mode 100644 index 163eaf965dd..00000000000 --- a/tests/ui/needless_collect_indirect.fixed +++ /dev/null @@ -1,22 +0,0 @@ -// run-rustfix - -#[allow(unused)] -use std::collections::{HashMap, VecDeque}; - -fn main() { - let sample = [1; 5]; - let indirect_iter = sample.iter().collect::>(); - indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); - let indirect_len = sample.iter().collect::>(); - indirect_len.len(); - let indirect_empty = sample.iter().collect::>(); - indirect_empty.is_empty(); - let indirect_contains = sample.iter().collect::>(); - indirect_contains.contains(&&5); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .into_iter() - .map(|x| (*x, *x + 1)) - .collect::>(); -} diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 163eaf965dd..4cf03e82035 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,6 +1,3 @@ -// run-rustfix - -#[allow(unused)] use std::collections::{HashMap, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 700c73b0b22..0c1e61d7496 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:8:5 + --> $DIR/needless_collect_indirect.rs:5:5 | LL | / let indirect_iter = sample.iter().collect::>(); LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); @@ -13,7 +13,7 @@ LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:10:5 + --> $DIR/needless_collect_indirect.rs:7:5 | LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); @@ -26,7 +26,7 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:12:5 + --> $DIR/needless_collect_indirect.rs:9:5 | LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); @@ -39,7 +39,7 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:14:5 + --> $DIR/needless_collect_indirect.rs:11:5 | LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); From e521c67e5f29ddd84e0ce744dfc27a836d349514 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Mon, 3 Aug 2020 12:32:23 +0200 Subject: [PATCH 0343/1110] early return on empty parameters/where clause --- clippy_lints/src/trait_bounds.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 10811374875..06631f89f27 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -148,7 +148,7 @@ impl TraitBounds { } fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if in_macro(gen.span) { + if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() { return; } From 0e44ed5ca9b1436c245a4660b04e76fd99be7420 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 3 Aug 2020 17:22:47 +0200 Subject: [PATCH 0344/1110] Fix ui-cargo tests in CI --- tests/compile-test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index eb6d495acbe..911da40d27b 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -3,7 +3,7 @@ use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; -use std::env::{self, set_var}; +use std::env::{self, set_var, var}; use std::ffi::OsStr; use std::fs; use std::io; @@ -136,7 +136,9 @@ fn run_ui_toml(config: &mut compiletest::Config) { let tests = compiletest::make_tests(&config); + let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); let res = run_tests(&config, tests); + set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), From 25abd7ae76e2a708dda5487119c20af3be64edb7 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 8 Jul 2020 20:29:56 -0700 Subject: [PATCH 0345/1110] Create stable_sort_primitive lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/stable_sort_primitive.rs | 130 ++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 30 +++++ src/lintlist/mod.rs | 7 ++ tests/ui/stable_sort_primitive.fixed | 32 ++++++ tests/ui/stable_sort_primitive.rs | 32 ++++++ tests/ui/stable_sort_primitive.stderr | 46 ++++++++ tests/ui/unnecessary_sort_by.fixed | 2 + tests/ui/unnecessary_sort_by.rs | 2 + tests/ui/unnecessary_sort_by.stderr | 14 +-- 11 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 clippy_lints/src/stable_sort_primitive.rs create mode 100644 tests/ui/stable_sort_primitive.fixed create mode 100644 tests/ui/stable_sort_primitive.rs create mode 100644 tests/ui/stable_sort_primitive.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..43a32e828d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1701,6 +1701,7 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..9fc07e07fd3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -288,6 +288,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod stable_sort_primitive; mod strings; mod suspicious_trait_impl; mod swap; @@ -776,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -1078,6 +1080,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1408,6 +1411,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1723,6 +1727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mutex_atomic::MUTEX_ATOMIC), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs new file mode 100644 index 00000000000..c48da004a60 --- /dev/null +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -0,0 +1,130 @@ +use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; + +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// When sorting primitive values (integers, bools, chars, as well + /// as arrays, slices, and tuples of such items), it is better to + /// use an unstable sort than a stable sort. + /// + /// **Why is this bad?** + /// Using a stable sort consumes more memory and cpu cycles. Because + /// values which compare equal are identical, preserving their + /// relative order (the guarantee that a stable sort provides) means + /// nothing, while the extra costs still apply. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort(); + /// ``` + /// Use instead: + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort_unstable(); + /// ``` + pub STABLE_SORT_PRIMITIVE, + perf, + "use of sort() when sort_unstable() is equivalent" +} + +declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); + +/// The three "kinds" of sorts +enum SortingKind { + Vanilla, + // The other kinds of lint are currently commented out because they + // can map distinct values to equal ones. If the key function is + // provably one-to-one, or if the Cmp function conserves equality, + // then they could be linted on, but I don't know if we can check + // for that. + + // ByKey, + // ByCmp, +} +impl SortingKind { + /// The name of the stable version of this kind of sort + fn stable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort", + // SortingKind::ByKey => "sort_by_key", + // SortingKind::ByCmp => "sort_by", + } + } + /// The name of the unstable version of this kind of sort + fn unstable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort_unstable", + // SortingKind::ByKey => "sort_unstable_by_key", + // SortingKind::ByCmp => "sort_unstable_by", + } + } + /// Takes the name of a function call and returns the kind of sort + /// that corresponds to that function name (or None if it isn't) + fn from_stable_name(name: &str) -> Option { + match name { + "sort" => Some(SortingKind::Vanilla), + // "sort_by" => Some(SortingKind::ByCmp), + // "sort_by_key" => Some(SortingKind::ByKey), + _ => None, + } + } +} + +/// A detected instance of this lint +struct LintDetection { + slice_name: String, + method: SortingKind, + method_args: String, +} + +fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(slice) = &args.get(0); + if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); + if is_slice_of_primitives(cx, slice); + then { + let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + } else { + None + } + } +} + +impl LateLintPass<'_> for StableSortPrimitive { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(detection) = detect_stable_sort_primitive(cx, expr) { + span_lint_and_sugg( + cx, + STABLE_SORT_PRIMITIVE, + expr.span, + format!( + "Use {} instead of {}", + detection.method.unstable_name(), + detection.method.stable_name() + ) + .as_str(), + "try", + format!( + "{}.{}({})", + detection.slice_name, + detection.method.unstable_name(), + detection.method_args + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 655b1133cf7..c75f8042907 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1378,6 +1378,36 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo }) } +/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point +/// number type, a str, or an array, slice, or tuple of those types). +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, + ty::Ref(_, inner, _) if inner.kind == ty::Str => true, + ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), + ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), + _ => false, + } +} + +/// Returns true iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function). +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty_adjusted(expr); + match expr_type.kind { + ty::Slice(ref element_type) + | ty::Ref( + _, + ty::TyS { + kind: ty::Slice(ref element_type), + .. + }, + _, + ) => is_recursively_primitive_type(element_type), + _ => false, + } +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..41d06a67881 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2026,6 +2026,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "stable_sort_primitive", + group: "perf", + desc: "use of sort() when sort_unstable() is equivalent", + deprecation: None, + module: "stable_sort_primitive", + }, Lint { name: "string_add", group: "restriction", diff --git a/tests/ui/stable_sort_primitive.fixed b/tests/ui/stable_sort_primitive.fixed new file mode 100644 index 00000000000..8f8f5665931 --- /dev/null +++ b/tests/ui/stable_sort_primitive.fixed @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort_unstable(); + let mut vec = vec![false, false, true]; + vec.sort_unstable(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort_unstable(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort_unstable(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort_unstable(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort_unstable(); + let mut arr = [1, 3, 2]; + arr.sort_unstable(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/tests/ui/stable_sort_primitive.rs b/tests/ui/stable_sort_primitive.rs new file mode 100644 index 00000000000..f9bd9779067 --- /dev/null +++ b/tests/ui/stable_sort_primitive.rs @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort(); + let mut vec = vec![false, false, true]; + vec.sort(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort(); + let mut arr = [1, 3, 2]; + arr.sort(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr new file mode 100644 index 00000000000..b0b729ede48 --- /dev/null +++ b/tests/ui/stable_sort_primitive.stderr @@ -0,0 +1,46 @@ +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:7:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:9:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:11:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:13:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:15:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:17:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:19:5 + | +LL | arr.sort(); + | ^^^^^^^^^^ help: try: `arr.sort_unstable()` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index c017d1cf9a4..31c2ba0f9c5 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 1929c72b2f2..a3c8ae468ed 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 903b6e5099c..70c6cf0a3b6 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,5 +1,5 @@ error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:12:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` @@ -7,37 +7,37 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:13:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:20:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:19:5 + --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` From e9677105bf85a2b0c57e8d67d2ed22a286333033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 2 Aug 2020 00:00:00 +0000 Subject: [PATCH 0346/1110] try_err: Consider Try impl for Poll when generating suggestions There are two different implementation of Try trait for Poll type; Poll> and Poll>>. Take them into account when generating suggestions. For example, for Err(e)? suggest either return Poll::Ready(Err(e)) or return Poll::Ready(Some(Err(e))) as appropriate. --- clippy_lints/src/try_err.rs | 112 +++++++++++++++++++++++++------- clippy_lints/src/utils/paths.rs | 2 +- tests/ui/try_err.fixed | 21 ++++++ tests/ui/try_err.rs | 21 ++++++ tests/ui/try_err.stderr | 30 +++++++-- 5 files changed, 155 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index d3b351f30ef..3bd73d9f21a 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,10 +1,13 @@ -use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg}; +use crate::utils::{ + is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; +use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let Some(ref err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; if match_qpath(err_fun_path, &paths::RESULT_ERR); - if let Some(return_type) = find_err_return_type(cx, &expr.kind); - + if let Some(return_ty) = find_return_type(cx, &expr.kind); then { - let err_type = cx.typeck_results().expr_ty(err_arg); + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let origin_snippet = if err_arg.span.from_expansion() { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") }; - let suggestion = if err_type == return_type { - format!("return Err({})", origin_snippet) + let suggestion = if err_ty == expr_err_ty { + format!("return {}{}{}", prefix, origin_snippet, suffix) } else { - format!("return Err({}.into())", origin_snippet) + format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; span_lint_and_sugg( @@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } } -// In order to determine whether to suggest `.into()` or not, we need to find the error type the -// function returns. To do that, we look for the From::from call (see tree above), and capture -// its output type. -fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { - arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty)) - } else { - None + for arm in arms.iter() { + if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } } + None } -// Check for From::from in one of the match arms. -fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option> { +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind; - if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind; - if let ExprKind::Path(ref from_error_fn) = from_error_path.kind; - if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR); - if let Some(from_error_arg) = from_error_args.get(0); + if let ty::Adt(_, subst) = ty.kind; + if is_type_diagnostic_item(cx, ty, sym!(result_type)); + let err_ty = subst.type_at(1); then { - Some(cx.typeck_results().expr_ty(from_error_arg)) + Some(err_ty) + } else { + None + } + } +} + +/// Extracts the error type from Poll>. +fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind; + if match_def_path(cx, def.did, &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind; + if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did); + let err_ty = ready_subst.type_at(1); + + then { + Some(err_ty) + } else { + None + } + } +} + +/// Extracts the error type from Poll>>. +fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind; + if match_def_path(cx, def.did, &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind; + if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did); + let some_ty = ready_subst.type_at(0); + + if let ty::Adt(some_def, some_subst) = some_ty.kind; + if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did); + let err_ty = some_subst.type_at(1); + + then { + Some(err_ty) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index a515ee29c82..923b319d777 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -80,6 +80,7 @@ pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; @@ -129,7 +130,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; -pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 29d9139d3e3..9e77dcd8731 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + } else if n == 1 { + return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))) + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into()))) + } + + Poll::Ready(None) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 5e85d091a2a..41bcb0a189e 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + Err(io::ErrorKind::WriteZero)? + } else if n == 1 { + Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + Err(io::ErrorKind::NotFound)? + } + + Poll::Ready(None) +} diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 21e9d4048a5..3f1cbc17e72 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:15:9 + --> $DIR/try_err.rs:18:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` @@ -11,28 +11,46 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:25:9 + --> $DIR/try_err.rs:28:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:45:17 + --> $DIR/try_err.rs:48:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:64:17 + --> $DIR/try_err.rs:67:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:103:9 + --> $DIR/try_err.rs:106:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` -error: aborting due to 5 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:113:9 + | +LL | Err(io::ErrorKind::WriteZero)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:115:9 + | +LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:123:9 + | +LL | Err(io::ErrorKind::NotFound)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` + +error: aborting due to 8 previous errors From 0ccdf2913a335c4f0b34098f84baeeb3fc852255 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 3 Aug 2020 16:23:20 -0500 Subject: [PATCH 0347/1110] Remove obsolete known problems unnecessary_fold --- clippy_lints/src/methods/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9edcdd979ff..ff8da6b4015 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1052,8 +1052,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability. /// - /// **Known problems:** False positive in pattern guards. Will be resolved once - /// non-lexical lifetimes are stable. + /// **Known problems:** None. /// /// **Example:** /// ```rust From 542740c2eceff2369b2ac44e891a37313dd1785c Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 4 Aug 2020 17:53:29 -0700 Subject: [PATCH 0348/1110] Run cargo dev fmt --- clippy_lints/src/stable_sort_primitive.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index c48da004a60..cd7056620a2 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -43,30 +43,30 @@ declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); /// The three "kinds" of sorts enum SortingKind { Vanilla, - // The other kinds of lint are currently commented out because they - // can map distinct values to equal ones. If the key function is - // provably one-to-one, or if the Cmp function conserves equality, - // then they could be linted on, but I don't know if we can check - // for that. + /* The other kinds of lint are currently commented out because they + * can map distinct values to equal ones. If the key function is + * provably one-to-one, or if the Cmp function conserves equality, + * then they could be linted on, but I don't know if we can check + * for that. */ - // ByKey, - // ByCmp, + /* ByKey, + * ByCmp, */ } impl SortingKind { /// The name of the stable version of this kind of sort fn stable_name(&self) -> &str { match self { SortingKind::Vanilla => "sort", - // SortingKind::ByKey => "sort_by_key", - // SortingKind::ByCmp => "sort_by", + /* SortingKind::ByKey => "sort_by_key", + * SortingKind::ByCmp => "sort_by", */ } } /// The name of the unstable version of this kind of sort fn unstable_name(&self) -> &str { match self { SortingKind::Vanilla => "sort_unstable", - // SortingKind::ByKey => "sort_unstable_by_key", - // SortingKind::ByCmp => "sort_unstable_by", + /* SortingKind::ByKey => "sort_unstable_by_key", + * SortingKind::ByCmp => "sort_unstable_by", */ } } /// Takes the name of a function call and returns the kind of sort From 1e8ada3cab5470a4f2070c0aa9d1a94922476621 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 18 Jul 2020 23:28:31 +0900 Subject: [PATCH 0349/1110] Add lint `same_item_push` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/loops.rs | 265 +++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/same_item_push.rs | 77 ++++++++++ tests/ui/same_item_push.stderr | 35 +++++ 6 files changed, 388 insertions(+) create mode 100644 tests/ui/same_item_push.rs create mode 100644 tests/ui/same_item_push.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d83d978b8..fbc783f1c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1687,6 +1687,7 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26aff6af8cd..2bd5ae0ed98 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -610,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, + &loops::SAME_ITEM_PUSH, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, @@ -1405,6 +1406,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1543,6 +1545,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6359c20040c..48891a59c00 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -419,6 +419,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop is being used to push a constant + /// value into a Vec. + /// + /// **Why is this bad?** This kind of operation can be expressed more succinctly with + /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// have better performance. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = Vec::new(); + /// for _ in 0..20 { + /// vec.push(item1); + /// } + /// for _ in 0..30 { + /// vec.push(item2); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = vec![item1; 20]; + /// vec.resize(20 + 30, item2); + /// ``` + pub SAME_ITEM_PUSH, + style, + "the same item is pushed inside of a for loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -435,6 +468,7 @@ declare_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + SAME_ITEM_PUSH, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -740,6 +774,7 @@ fn check_for_loop<'tcx>( check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); detect_manual_memcpy(cx, pat, arg, body, expr); + detect_same_item_push(cx, pat, arg, body, expr); } fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { @@ -1016,6 +1051,236 @@ fn detect_manual_memcpy<'tcx>( } } +// Delegate that traverses expression and detects mutable variables being used +struct UsesMutableDelegate { + found_mutable: bool, +} + +impl<'tcx> Delegate<'tcx> for UsesMutableDelegate { + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} + + fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { + // Mutable variable is found + if let ty::BorrowKind::MutBorrow = bk { + self.found_mutable = true; + } + } + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>) {} +} + +// Uses UsesMutableDelegate to find mutable variables in an expression expr +fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + let mut delegate = UsesMutableDelegate { found_mutable: false }; + let def_id = expr.hir_id.owner.to_def_id(); + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new( + &mut delegate, + &infcx, + def_id.expect_local(), + cx.param_env, + cx.tables(), + ).walk_expr(expr); + }); + + delegate.found_mutable +} + +// Scans for the usage of the for loop pattern +struct ForPatternVisitor<'a, 'tcx> { + found_pattern: bool, + // Pattern that we are searching for + for_pattern: &'a Pat<'tcx>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // Recursively explore an expression until a ExprKind::Path is found + match &expr.kind { + ExprKind::Array(expr_list) | ExprKind::MethodCall(_, _, expr_list, _) | ExprKind::Tup(expr_list) => { + for expr in *expr_list { + self.visit_expr(expr) + } + }, + ExprKind::Binary(_, lhs_expr, rhs_expr) => { + self.visit_expr(lhs_expr); + self.visit_expr(rhs_expr); + }, + ExprKind::Box(expr) + | ExprKind::Unary(_, expr) + | ExprKind::Cast(expr, _) + | ExprKind::Type(expr, _) + | ExprKind::AddrOf(_, _, expr) + | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), + _ => { + // Exploration cannot continue ... calculate the hir_id of the current + // expr assuming it is a Path + if let Some(hir_id) = var_def_id(self.cx, &expr) { + // Pattern is found + if hir_id == self.for_pattern.hir_id { + self.found_pattern = true; + } + // If the for loop pattern is a tuple, determine whether the current + // expr is inside that tuple pattern + if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { + let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); + if hir_id_list.contains(&hir_id) { + self.found_pattern = true; + } + } + } + }, + } + } + + // This is triggered by walk_expr() for the case of vec.push(pat) + fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, _: HirId, _: Span) { + if_chain! { + if let QPath::Resolved(_, path) = qpath; + if let Res::Local(hir_id) = &path.res; + then { + if *hir_id == self.for_pattern.hir_id{ + self.found_pattern = true; + } + + if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { + let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); + if hir_id_list.contains(&hir_id) { + self.found_pattern = true; + } + } + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Scans the body of the for loop and determines whether lint should be given +struct SameItemPushVisitor<'a, 'tcx> { + should_lint: bool, + // this field holds the last vec push operation visited, which should be the only push seen + vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + // Non-determinism may occur ... don't give a lint + ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false, + ExprKind::Block(block, _) => self.visit_block(block), + _ => {}, + } + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + for stmt in b.stmts.iter() { + self.visit_stmt(stmt); + } + } + + fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) { + let vec_push_option = get_vec_push(self.cx, s); + if vec_push_option.is_none() { + // Current statement is not a push so visit inside + match &s.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr), + _ => {}, + } + } else { + // Current statement is a push ...check whether another + // push had been previously done + if self.vec_push.is_none() { + self.vec_push = vec_push_option; + } else { + // There are multiple pushes ... don't lint + self.should_lint = false; + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Given some statement, determine if that statement is a push on a Vec. If it is, return +// the Vec being pushed into and the item being pushed +fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Extract method being called + if let StmtKind::Semi(semi_stmt) = &stmt.kind; + if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind; + // Figure out the parameters for the method call + if let Some(self_expr) = args.get(0); + if let Some(pushed_item) = args.get(1); + // Check that the method being called is push() on a Vec + if match_type(cx, cx.tables().expr_ty(self_expr), &paths::VEC); + if path.ident.name.as_str() == "push"; + then { + return Some((self_expr, pushed_item)) + } + } + None +} + +/// Detects for loop pushing the same item into a Vec +fn detect_same_item_push<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + _: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + _: &'tcx Expr<'_>, +) { + // Determine whether it is safe to lint the body + let mut same_item_push_visitor = SameItemPushVisitor { + should_lint: true, + vec_push: None, + cx, + }; + walk_expr(&mut same_item_push_visitor, body); + if same_item_push_visitor.should_lint { + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { + // Make sure that the push does not involve possibly mutating values + if !has_mutable_variables(cx, pushed_item) { + // Walk through the expression being pushed and make sure that it + // does not contain the for loop pattern + let mut for_pat_visitor = ForPatternVisitor { + found_pattern: false, + for_pattern: pat, + cx, + }; + walk_expr(&mut for_pat_visitor, pushed_item); + + if !for_pat_visitor.found_pattern { + let vec_str = snippet(cx, vec.span, ""); + let item_str = snippet(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } + } +} + /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. #[allow(clippy::too_many_lines)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a08d7da6dcb..1b10226c930 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1935,6 +1935,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copies", }, + Lint { + name: "same_item_push", + group: "style", + desc: "default lint description", + deprecation: None, + module: "same_item_push", + }, Lint { name: "search_is_some", group: "complexity", diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs new file mode 100644 index 00000000000..e3a5a647f76 --- /dev/null +++ b/tests/ui/same_item_push.rs @@ -0,0 +1,77 @@ +#![warn(clippy::same_item_push)] + +fn mutate_increment(x: &mut u8) -> u8 { + *x += 1; + *x +} + +fn increment(x: u8) -> u8 { + x + 1 +} + +fn main() { + // Test for basic case + let mut spaces = Vec::with_capacity(10); + for _ in 0..10 { + spaces.push(vec![b' ']); + } + + let mut vec2: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec2.push(item); + } + + let mut vec3: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec3.push(item); + } + + let mut vec4: Vec = Vec::new(); + for _ in 0..15 { + vec4.push(13); + } + + // Suggestion should not be given as pushed variable can mutate + let mut vec5: Vec = Vec::new(); + let mut item: u8 = 2; + for _ in 0..30 { + vec5.push(mutate_increment(&mut item)); + } + + let mut vec6: Vec = Vec::new(); + let mut item: u8 = 2; + let mut item2 = &mut mutate_increment(&mut item); + for _ in 0..30 { + vec6.push(mutate_increment(item2)); + } + + let mut vec7: Vec = Vec::new(); + for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { + vec7.push(a); + } + + let mut vec8: Vec = Vec::new(); + for i in 0..30 { + vec8.push(increment(i)); + } + + let mut vec9: Vec = Vec::new(); + for i in 0..30 { + vec9.push(i + i * i); + } + + // Suggestion should not be given as there are multiple pushes that are not the same + let mut vec10: Vec = Vec::new(); + let item: u8 = 2; + for _ in 0..30 { + vec10.push(item); + vec10.push(item * 2); + } + + // Suggestion should not be given as Vec is not involved + for _ in 0..5 { + println!("Same Item Push"); + } +} diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr new file mode 100644 index 00000000000..559cc450b9d --- /dev/null +++ b/tests/ui/same_item_push.stderr @@ -0,0 +1,35 @@ +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:16:9 + | +LL | spaces.push(vec![b' ']); + | ^^^^^^ + | + = note: `-D clippy::same-item-push` implied by `-D warnings` + = help: try using vec![<[_]>::into_vec(box [$($x),+]);SIZE] or spaces.resize(NEW_SIZE, <[_]>::into_vec(box [$($x),+])) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:22:9 + | +LL | vec2.push(item); + | ^^^^ + | + = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:28:9 + | +LL | vec3.push(item); + | ^^^^ + | + = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:33:9 + | +LL | vec4.push(13); + | ^^^^ + | + = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + +error: aborting due to 4 previous errors + From 161f47510076d36722546c3541a546f9b724fadd Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 19 Jul 2020 23:43:35 +0900 Subject: [PATCH 0350/1110] Add test case for `same_item_push` --- clippy_lints/src/loops.rs | 1 + tests/ui/same_item_push.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 48891a59c00..1c2f1225497 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1114,6 +1114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { | ExprKind::Cast(expr, _) | ExprKind::Type(expr, _) | ExprKind::AddrOf(_, _, expr) + | ExprKind::Field(expr, _) | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), _ => { // Exploration cannot continue ... calculate the hir_id of the current diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index e3a5a647f76..4bb5e73ff0d 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -74,4 +74,16 @@ fn main() { for _ in 0..5 { println!("Same Item Push"); } + + struct A { + kind: u32, + } + let mut vec_a: Vec = Vec::new(); + for i in 0..30 { + vec_a.push(A{kind: i}); + } + let mut vec12: Vec = Vec::new(); + for a in vec_a { + vec12.push(2u8.pow(a.kind)); + } } From 2beb9090d1b9adb2b0930da511bf1750e570905b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 20 Jul 2020 22:40:31 +0900 Subject: [PATCH 0351/1110] Rename TypeckTables to TypeckResults --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c2f1225497..766c68df623 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1079,7 +1079,7 @@ fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> &infcx, def_id.expect_local(), cx.param_env, - cx.tables(), + cx.typeck_results(), ).walk_expr(expr); }); @@ -1224,7 +1224,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if match_type(cx, cx.tables().expr_ty(self_expr), &paths::VEC); + if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC); if path.ident.name.as_str() == "push"; then { return Some((self_expr, pushed_item)) From 1543e117cc7459bef2b57389503f0f526a903f45 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 20 Jul 2020 22:52:30 +0900 Subject: [PATCH 0352/1110] cargo dev update_lints --- clippy_lints/src/lib.rs | 6 +++--- src/lintlist/mod.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2bd5ae0ed98..308868fc90a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -607,10 +607,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_COLLECT, &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, + &loops::SAME_ITEM_PUSH, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, - &loops::SAME_ITEM_PUSH, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, @@ -1293,6 +1293,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_COLLECT), LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1406,7 +1407,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1495,6 +1495,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), @@ -1545,7 +1546,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b10226c930..1f79e44049f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1938,9 +1938,9 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "same_item_push", group: "style", - desc: "default lint description", + desc: "the same item is pushed inside of a for loop", deprecation: None, - module: "same_item_push", + module: "loops", }, Lint { name: "search_is_some", From 14a4e3bcc8082b0323886ae15365ea2424b512cf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 21 Jul 2020 08:15:13 +0900 Subject: [PATCH 0353/1110] Fix a lint message --- clippy_lints/src/loops.rs | 11 ++++++----- tests/ui/same_item_push.stderr | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 766c68df623..8ca67cae0e9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,9 +3,10 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, span_lint, + get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, + snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; @@ -1262,8 +1263,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut for_pat_visitor, pushed_item); if !for_pat_visitor.found_pattern { - let vec_str = snippet(cx, vec.span, ""); - let item_str = snippet(cx, pushed_item.span, ""); + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); span_lint_and_help( cx, diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 559cc450b9d..ddc5d48cd41 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -5,7 +5,7 @@ LL | spaces.push(vec![b' ']); | ^^^^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![<[_]>::into_vec(box [$($x),+]);SIZE] or spaces.resize(NEW_SIZE, <[_]>::into_vec(box [$($x),+])) + = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:22:9 From b7ceb4d3d7ed3ea7039caf803073e86ad3643e21 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 21 Jul 2020 08:25:11 +0900 Subject: [PATCH 0354/1110] rustfmt --- clippy_lints/src/loops.rs | 5 +++-- tests/ui/same_item_push.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8ca67cae0e9..3a1fbc4bfed 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1081,7 +1081,8 @@ fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> def_id.expect_local(), cx.param_env, cx.typeck_results(), - ).walk_expr(expr); + ) + .walk_expr(expr); }); delegate.found_mutable @@ -1271,7 +1272,7 @@ fn detect_same_item_push<'tcx>( SAME_ITEM_PUSH, vec.span, "it looks like the same item is being pushed into this Vec", - None, + None, &format!( "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", item_str, vec_str, item_str diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 4bb5e73ff0d..ff1088f86f6 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -80,7 +80,7 @@ fn main() { } let mut vec_a: Vec = Vec::new(); for i in 0..30 { - vec_a.push(A{kind: i}); + vec_a.push(A { kind: i }); } let mut vec12: Vec = Vec::new(); for a in vec_a { From 228f668282daab05ec20adbbdeb227e923d10864 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 22 Jul 2020 22:11:31 +0900 Subject: [PATCH 0355/1110] Use `mutated_variables` --- clippy_lints/src/loops.rs | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3a1fbc4bfed..86952c10dfc 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1052,42 +1052,6 @@ fn detect_manual_memcpy<'tcx>( } } -// Delegate that traverses expression and detects mutable variables being used -struct UsesMutableDelegate { - found_mutable: bool, -} - -impl<'tcx> Delegate<'tcx> for UsesMutableDelegate { - fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} - - fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { - // Mutable variable is found - if let ty::BorrowKind::MutBorrow = bk { - self.found_mutable = true; - } - } - - fn mutate(&mut self, _: &PlaceWithHirId<'tcx>) {} -} - -// Uses UsesMutableDelegate to find mutable variables in an expression expr -fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - let mut delegate = UsesMutableDelegate { found_mutable: false }; - let def_id = expr.hir_id.owner.to_def_id(); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - def_id.expect_local(), - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(expr); - }); - - delegate.found_mutable -} - // Scans for the usage of the for loop pattern struct ForPatternVisitor<'a, 'tcx> { found_pattern: bool, @@ -1253,7 +1217,7 @@ fn detect_same_item_push<'tcx>( if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values - if !has_mutable_variables(cx, pushed_item) { + if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { // Walk through the expression being pushed and make sure that it // does not contain the for loop pattern let mut for_pat_visitor = ForPatternVisitor { From e48685edef9889d7c0ae391cf050f878d228ae25 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 22 Jul 2020 23:22:17 +0900 Subject: [PATCH 0356/1110] Just check if it contains `_` in `for pat` --- clippy_lints/src/loops.rs | 87 +-------------------------------------- 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 86952c10dfc..3104f0c137e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1052,82 +1052,6 @@ fn detect_manual_memcpy<'tcx>( } } -// Scans for the usage of the for loop pattern -struct ForPatternVisitor<'a, 'tcx> { - found_pattern: bool, - // Pattern that we are searching for - for_pattern: &'a Pat<'tcx>, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // Recursively explore an expression until a ExprKind::Path is found - match &expr.kind { - ExprKind::Array(expr_list) | ExprKind::MethodCall(_, _, expr_list, _) | ExprKind::Tup(expr_list) => { - for expr in *expr_list { - self.visit_expr(expr) - } - }, - ExprKind::Binary(_, lhs_expr, rhs_expr) => { - self.visit_expr(lhs_expr); - self.visit_expr(rhs_expr); - }, - ExprKind::Box(expr) - | ExprKind::Unary(_, expr) - | ExprKind::Cast(expr, _) - | ExprKind::Type(expr, _) - | ExprKind::AddrOf(_, _, expr) - | ExprKind::Field(expr, _) - | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), - _ => { - // Exploration cannot continue ... calculate the hir_id of the current - // expr assuming it is a Path - if let Some(hir_id) = var_def_id(self.cx, &expr) { - // Pattern is found - if hir_id == self.for_pattern.hir_id { - self.found_pattern = true; - } - // If the for loop pattern is a tuple, determine whether the current - // expr is inside that tuple pattern - if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { - let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); - if hir_id_list.contains(&hir_id) { - self.found_pattern = true; - } - } - } - }, - } - } - - // This is triggered by walk_expr() for the case of vec.push(pat) - fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, _: HirId, _: Span) { - if_chain! { - if let QPath::Resolved(_, path) = qpath; - if let Res::Local(hir_id) = &path.res; - then { - if *hir_id == self.for_pattern.hir_id{ - self.found_pattern = true; - } - - if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { - let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); - if hir_id_list.contains(&hir_id) { - self.found_pattern = true; - } - } - } - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - // Scans the body of the for loop and determines whether lint should be given struct SameItemPushVisitor<'a, 'tcx> { should_lint: bool, @@ -1218,16 +1142,7 @@ fn detect_same_item_push<'tcx>( if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - // Walk through the expression being pushed and make sure that it - // does not contain the for loop pattern - let mut for_pat_visitor = ForPatternVisitor { - found_pattern: false, - for_pattern: pat, - cx, - }; - walk_expr(&mut for_pat_visitor, pushed_item); - - if !for_pat_visitor.found_pattern { + if let PatKind::Wild = pat.kind { let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); From 610d4e3c8b1bfa27e059043554f4156fe1254142 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 5 Aug 2020 23:10:30 +0900 Subject: [PATCH 0357/1110] rustfmt --- clippy_lints/src/loops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3104f0c137e..5c918ff648f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,11 +3,11 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, - implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, - last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, - snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, + get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; From 50a86d492718f2ad5e653575d19324205fa007f1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 6 Aug 2020 00:40:11 +0200 Subject: [PATCH 0358/1110] enable #[allow(clippy::unsafe_derive_deserialize)] --- clippy_lints/src/derive.rs | 8 +++++--- tests/ui/unsafe_derive_deserialize.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 08d8100a885..80a06758982 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, - span_lint_and_then, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help, + span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -354,7 +354,9 @@ fn check_unsafe_derive_deserialize<'tcx>( if_chain! { if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); if let ty::Adt(def, _) = ty.kind; - if def.did.is_local(); + if let Some(local_def_id) = def.did.as_local(); + let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); if cx.tcx.inherent_impls(def.did) .iter() .map(|imp_did| item_from_def_id(cx, *imp_did)) diff --git a/tests/ui/unsafe_derive_deserialize.rs b/tests/ui/unsafe_derive_deserialize.rs index 7bee9c499e1..690d705573d 100644 --- a/tests/ui/unsafe_derive_deserialize.rs +++ b/tests/ui/unsafe_derive_deserialize.rs @@ -57,4 +57,14 @@ impl E { #[derive(Deserialize)] pub struct F {} +// Check that we honor the `allow` attribute on the ADT +#[allow(clippy::unsafe_derive_deserialize)] +#[derive(Deserialize)] +pub struct G {} +impl G { + pub fn unsafe_block(&self) { + unsafe {} + } +} + fn main() {} From 0abc4833e5dc8ec4da48d5b25e1d0df81cceec4d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Thu, 6 Aug 2020 02:42:40 +0200 Subject: [PATCH 0359/1110] Lint .min(x).max(y) with x < y Fixes #5854 --- clippy_lints/src/minmax.rs | 52 ++++++++++++++++++++++++-------------- tests/ui/min_max.rs | 14 ++++++++++ tests/ui/min_max.stderr | 32 ++++++++++++++++++++++- 3 files changed, 78 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index dae39aaf5e2..1f798fd1120 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -18,6 +18,10 @@ declare_clippy_lint! { /// ```ignore /// min(0, max(100, x)) /// ``` + /// or + /// ```ignore + /// x.max(100).min(0) + /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value /// between 0 and 100, but has erroneously swapped `min` and `max`. pub MIN_MAX, @@ -60,25 +64,35 @@ enum MinMax { } fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { - if let ExprKind::Call(ref path, ref args) = expr.kind { - if let ExprKind::Path(ref qpath) = path.kind { - cx.typeck_results() - .qpath_res(qpath, path.hir_id) - .opt_def_id() - .and_then(|def_id| { - if match_def_path(cx, def_id, &paths::CMP_MIN) { - fetch_const(cx, args, MinMax::Min) - } else if match_def_path(cx, def_id, &paths::CMP_MAX) { - fetch_const(cx, args, MinMax::Max) - } else { - None - } - }) - } else { - None - } - } else { - None + match expr.kind { + ExprKind::Call(ref path, ref args) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.typeck_results() + .qpath_res(qpath, path.hir_id) + .opt_def_id() + .and_then(|def_id| { + if match_def_path(cx, def_id, &paths::CMP_MIN) { + fetch_const(cx, args, MinMax::Min) + } else if match_def_path(cx, def_id, &paths::CMP_MAX) { + fetch_const(cx, args, MinMax::Max) + } else { + None + } + }) + } else { + None + } + }, + ExprKind::MethodCall(ref path, _, ref args, _) => { + if path.ident.as_str() == sym!(max).as_str() { + fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) + } else { + None + } + }, + _ => None, } } diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 8307d4b3019..90ec5676493 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -30,4 +30,18 @@ fn main() { max(min(s, "Apple"), "Zoo"); max("Apple", min(s, "Zoo")); // ok + + x.min(1).max(3); + x.max(3).min(1); + + x.max(1).min(3); // ok + x.min(3).max(1); // ok + + max(x.min(1), 3); + min(x.max(1), 3); // ok + + s.max("Zoo").min("Apple"); + s.min("Apple").max("Zoo"); + + s.min("Zoo").max("Apple"); // ok } diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index b552c137f7c..653946dc025 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -42,5 +42,35 @@ error: this `min`/`max` combination leads to constant result LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:34:5 + | +LL | x.min(1).max(3); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:35:5 + | +LL | x.max(3).min(1); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:40:5 + | +LL | max(x.min(1), 3); + | ^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:43:5 + | +LL | s.max("Zoo").min("Apple"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:44:5 + | +LL | s.min("Apple").max("Zoo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors From e0a4988fcc716e349fd801d98182c0d984a2ee3f Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 4 Aug 2020 20:23:14 +0200 Subject: [PATCH 0360/1110] Lint against `Self` as an arbitrary self type Fixes #5861 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/needless_fn_self_type.rs | 64 +++++++++++++++++++++++ clippy_lints/src/trait_bounds.rs | 2 +- src/lintlist/mod.rs | 7 +++ tests/ui/needless_fn_self_type.rs | 26 +++++++++ tests/ui/needless_fn_self_type.stderr | 11 ++++ 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/needless_fn_self_type.rs create mode 100644 tests/ui/needless_fn_self_type.rs create mode 100644 tests/ui/needless_fn_self_type.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe896d5efa..179ecee03da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1622,6 +1622,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_fn_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_fn_self_type [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 81864340f1a..80c85e70e89 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -254,6 +254,7 @@ mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; +mod needless_fn_self_type; mod needless_pass_by_value; mod needless_update; mod neg_cmp_op_on_partial_ord; @@ -722,6 +723,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, + &needless_fn_self_type::NEEDLESS_FN_SELF_TYPE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, @@ -1027,6 +1029,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box needless_fn_self_type::NeedlessFnSelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1374,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1534,6 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs new file mode 100644 index 00000000000..050a03467fb --- /dev/null +++ b/clippy_lints/src/needless_fn_self_type.rs @@ -0,0 +1,64 @@ +use crate::utils::span_lint_and_help; +use if_chain::if_chain; +use rustc_ast::ast::{Param, TyKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` fn fn parameters that explicitly + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_FN_SELF_TYPE, + style, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessFnSelfType => [NEEDLESS_FN_SELF_TYPE]); + +impl EarlyLintPass for NeedlessFnSelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if_chain! { + if p.is_self(); + if let TyKind::Path(None, path) = &p.ty.kind; + if let Some(segment) = path.segments.first(); + if segment.ident.as_str() == sym!(Self).as_str(); + then { + span_lint_and_help( + cx, + NEEDLESS_FN_SELF_TYPE, + p.ty.span, + "the type of the `self` parameter is already by default `Self`", + None, + "consider removing the type specification", + ); + } + } + } +} diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 06631f89f27..d4acf8df46d 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// fn func(arg: T) {} /// ``` /// or - /// /// + /// /// ```rust /// fn func(arg: T) where T: Clone + Default {} /// ``` diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b64c6e54409..a20e410f79b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1501,6 +1501,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "doc", }, + Lint { + name: "needless_fn_self_type", + group: "style", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_fn_self_type", + }, Lint { name: "needless_lifetimes", group: "complexity", diff --git a/tests/ui/needless_fn_self_type.rs b/tests/ui/needless_fn_self_type.rs new file mode 100644 index 00000000000..12bb84582ff --- /dev/null +++ b/tests/ui/needless_fn_self_type.rs @@ -0,0 +1,26 @@ +#![warn(clippy::style, clippy::needless_fn_self_type)] + +pub enum ValType { + I32, + I64, + F32, + F64, +} + +impl ValType { + pub fn bytes_bad(self: Self) -> usize { + match self { + Self::I32 | Self::F32 => 4, + Self::I64 | Self::F64 => 8, + } + } + + pub fn bytes_good(self) -> usize { + match self { + Self::I32 | Self::F32 => 4, + Self::I64 | Self::F64 => 8, + } + } +} + +fn main() {} diff --git a/tests/ui/needless_fn_self_type.stderr b/tests/ui/needless_fn_self_type.stderr new file mode 100644 index 00000000000..703705c7842 --- /dev/null +++ b/tests/ui/needless_fn_self_type.stderr @@ -0,0 +1,11 @@ +error: the type of the `self` parameter is already by default `Self` + --> $DIR/needless_fn_self_type.rs:11:28 + | +LL | pub fn bytes_bad(self: Self) -> usize { + | ^^^^ + | + = note: `-D clippy::needless-fn-self-type` implied by `-D warnings` + = help: consider removing the type specification + +error: aborting due to previous error + From 737f62cb6eaa5eca23701dbbe8d63465e1c4843b Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 4 Aug 2020 21:07:35 +0200 Subject: [PATCH 0361/1110] fix doc --- clippy_lints/src/needless_fn_self_type.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs index 050a03467fb..b71f2fbbd46 100644 --- a/clippy_lints/src/needless_fn_self_type.rs +++ b/clippy_lints/src/needless_fn_self_type.rs @@ -13,6 +13,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// /// impl ValType { /// pub fn bytes(self: Self) -> usize { /// match self { @@ -26,6 +33,13 @@ declare_clippy_lint! { /// Could be rewritten as /// /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// /// impl ValType { /// pub fn bytes(self) -> usize { /// match self { From d635b76eaf3435f9bdce1dcbdd315b0e770493f0 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 02:59:30 +0200 Subject: [PATCH 0362/1110] adopt comments from review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +- .../src/needless_arbitrary_self_type.rs | 114 ++++++++++++++++++ clippy_lints/src/needless_fn_self_type.rs | 78 ------------ src/lintlist/mod.rs | 14 +-- tests/ui/needless_arbitrary_self_type.rs | 58 +++++++++ tests/ui/needless_arbitrary_self_type.stderr | 46 +++++++ tests/ui/needless_fn_self_type.rs | 26 ---- tests/ui/needless_fn_self_type.stderr | 11 -- 9 files changed, 231 insertions(+), 128 deletions(-) create mode 100644 clippy_lints/src/needless_arbitrary_self_type.rs delete mode 100644 clippy_lints/src/needless_fn_self_type.rs create mode 100644 tests/ui/needless_arbitrary_self_type.rs create mode 100644 tests/ui/needless_arbitrary_self_type.stderr delete mode 100644 tests/ui/needless_fn_self_type.rs delete mode 100644 tests/ui/needless_fn_self_type.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 179ecee03da..3f470f601ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1616,13 +1616,13 @@ Released 2018-09-13 [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main -[`needless_fn_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_fn_self_type [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 80c85e70e89..3c39de1abf1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -250,11 +250,11 @@ mod mut_mut; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; +mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; -mod needless_fn_self_type; mod needless_pass_by_value; mod needless_update; mod neg_cmp_op_on_partial_ord; @@ -718,12 +718,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, &mutex_atomic::MUTEX_ATOMIC, &mutex_atomic::MUTEX_INTEGER, + &needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, &needless_bool::BOOL_COMPARISON, &needless_bool::NEEDLESS_BOOL, &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, - &needless_fn_self_type::NEEDLESS_FN_SELF_TYPE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, @@ -1029,7 +1029,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); - store.register_early_pass(|| box needless_fn_self_type::NeedlessFnSelfType); + store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1374,10 +1374,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mutex_atomic::MUTEX_ATOMIC), + LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1538,7 +1538,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1607,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs new file mode 100644 index 00000000000..1b23ecd9ad2 --- /dev/null +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -0,0 +1,114 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` in fn parameters that + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_ARBITRARY_SELF_TYPE, + complexity, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]); + +enum Mode { + Ref(Option), + Value, +} + +fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { + if_chain! { + if let [segment] = &path.segments[..]; + if segment.ident.name == kw::SelfUpper; + then { + let self_param = match (binding_mode, mutbl) { + (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(None), Mutability::Not) => "&self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Value, Mutability::Mut) => "mut self".to_string(), + (Mode::Value, Mutability::Not) => "self".to_string(), + }; + + span_lint_and_sugg( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter is arbitrary", + "consider to change this parameter to", + self_param, + Applicability::MachineApplicable, + ) + } + } +} + +impl EarlyLintPass for NeedlessArbitrarySelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if !p.is_self() { + return; + } + + match &p.ty.kind { + TyKind::Path(None, path) => { + if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl) + } + }, + TyKind::Rptr(lifetime, mut_ty) => { + if let TyKind::Path(None, path) = &mut_ty.ty.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs deleted file mode 100644 index b71f2fbbd46..00000000000 --- a/clippy_lints/src/needless_fn_self_type.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::utils::span_lint_and_help; -use if_chain::if_chain; -use rustc_ast::ast::{Param, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** The lint checks for `self` fn fn parameters that explicitly - /// specify the `Self`-type explicitly - /// **Why is this bad?** Increases the amount and decreases the readability of code - /// - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// enum ValType { - /// I32, - /// I64, - /// F32, - /// F64, - /// } - /// - /// impl ValType { - /// pub fn bytes(self: Self) -> usize { - /// match self { - /// Self::I32 | Self::F32 => 4, - /// Self::I64 | Self::F64 => 8, - /// } - /// } - /// } - /// ``` - /// - /// Could be rewritten as - /// - /// ```rust - /// enum ValType { - /// I32, - /// I64, - /// F32, - /// F64, - /// } - /// - /// impl ValType { - /// pub fn bytes(self) -> usize { - /// match self { - /// Self::I32 | Self::F32 => 4, - /// Self::I64 | Self::F64 => 8, - /// } - /// } - /// } - /// ``` - pub NEEDLESS_FN_SELF_TYPE, - style, - "type of `self` parameter is already by default `Self`" -} - -declare_lint_pass!(NeedlessFnSelfType => [NEEDLESS_FN_SELF_TYPE]); - -impl EarlyLintPass for NeedlessFnSelfType { - fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if_chain! { - if p.is_self(); - if let TyKind::Path(None, path) = &p.ty.kind; - if let Some(segment) = path.segments.first(); - if segment.ident.as_str() == sym!(Self).as_str(); - then { - span_lint_and_help( - cx, - NEEDLESS_FN_SELF_TYPE, - p.ty.span, - "the type of the `self` parameter is already by default `Self`", - None, - "consider removing the type specification", - ); - } - } - } -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a20e410f79b..91761e8a86d 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1459,6 +1459,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bytecount", }, + Lint { + name: "needless_arbitrary_self_type", + group: "complexity", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_arbitrary_self_type", + }, Lint { name: "needless_bool", group: "complexity", @@ -1501,13 +1508,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "doc", }, - Lint { - name: "needless_fn_self_type", - group: "style", - desc: "type of `self` parameter is already by default `Self`", - deprecation: None, - module: "needless_fn_self_type", - }, Lint { name: "needless_lifetimes", group: "complexity", diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs new file mode 100644 index 00000000000..da4bbcf4a7d --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -0,0 +1,58 @@ +#![warn(clippy::needless_arbitrary_self_type)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self: Self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self: Self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(self: &Self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn mut_ref_bad(self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_bad(mut self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr new file mode 100644 index 00000000000..ee803b24071 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -0,0 +1,46 @@ +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:9:16 + | +LL | pub fn bad(self: Self) { + | ^^^^^^^^^^ help: consider to change this parameter to: `self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:17:20 + | +LL | pub fn mut_bad(mut self: Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:25:20 + | +LL | pub fn ref_bad(self: &Self) { + | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:29:38 + | +LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:37:24 + | +LL | pub fn mut_ref_bad(self: &mut Self) { + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:41:42 + | +LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:49:28 + | +LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { + | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_fn_self_type.rs b/tests/ui/needless_fn_self_type.rs deleted file mode 100644 index 12bb84582ff..00000000000 --- a/tests/ui/needless_fn_self_type.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![warn(clippy::style, clippy::needless_fn_self_type)] - -pub enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes_bad(self: Self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } - - pub fn bytes_good(self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} - -fn main() {} diff --git a/tests/ui/needless_fn_self_type.stderr b/tests/ui/needless_fn_self_type.stderr deleted file mode 100644 index 703705c7842..00000000000 --- a/tests/ui/needless_fn_self_type.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: the type of the `self` parameter is already by default `Self` - --> $DIR/needless_fn_self_type.rs:11:28 - | -LL | pub fn bytes_bad(self: Self) -> usize { - | ^^^^ - | - = note: `-D clippy::needless-fn-self-type` implied by `-D warnings` - = help: consider removing the type specification - -error: aborting due to previous error - From c87d999fa2f8e88f986aa5f4d76b708824e1fd3a Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 03:37:29 +0200 Subject: [PATCH 0363/1110] fix ui tests --- tests/ui/extra_unused_lifetimes.rs | 8 ++- tests/ui/extra_unused_lifetimes.stderr | 8 +-- tests/ui/len_without_is_empty.rs | 40 ++++++------- tests/ui/len_without_is_empty.stderr | 8 +-- tests/ui/len_zero.fixed | 20 +++---- tests/ui/len_zero.rs | 20 +++---- tests/ui/needless_arbitrary_self_type.fixed | 61 ++++++++++++++++++++ tests/ui/needless_arbitrary_self_type.rs | 3 + tests/ui/needless_arbitrary_self_type.stderr | 14 ++--- tests/ui/option_map_unit_fn_fixable.fixed | 4 +- tests/ui/option_map_unit_fn_fixable.rs | 4 +- tests/ui/result_map_unit_fn_fixable.fixed | 4 +- tests/ui/result_map_unit_fn_fixable.rs | 4 +- 13 files changed, 134 insertions(+), 64 deletions(-) create mode 100644 tests/ui/needless_arbitrary_self_type.fixed diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index ddbf4e98c51..150acfbfee7 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -1,4 +1,10 @@ -#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)] +#![allow( + unused, + dead_code, + clippy::needless_lifetimes, + clippy::needless_pass_by_value, + clippy::needless_arbitrary_self_type +)] #![warn(clippy::extra_unused_lifetimes)] fn empty() {} diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index 16bbb1c037d..ebdb8e74952 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -1,5 +1,5 @@ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:8:14 + --> $DIR/extra_unused_lifetimes.rs:14:14 | LL | fn unused_lt<'a>(x: u8) {} | ^^ @@ -7,19 +7,19 @@ LL | fn unused_lt<'a>(x: u8) {} = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:10:25 + --> $DIR/extra_unused_lifetimes.rs:16:25 | LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { | ^^ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:35:10 + --> $DIR/extra_unused_lifetimes.rs:41:10 | LL | fn x<'a>(&self) {} | ^^ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:61:22 + --> $DIR/extra_unused_lifetimes.rs:67:22 | LL | fn unused_lt<'a>(x: u8) {} | ^^ diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 3ef29dd6388..b5211318a15 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -4,14 +4,14 @@ pub struct PubOne; impl PubOne { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } } impl PubOne { // A second impl for this struct -- the error span shouldn't mention this. - pub fn irrelevant(self: &Self) -> bool { + pub fn irrelevant(&self) -> bool { false } } @@ -21,7 +21,7 @@ pub struct PubAllowed; #[allow(clippy::len_without_is_empty)] impl PubAllowed { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } } @@ -29,17 +29,17 @@ impl PubAllowed { // No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the // impl containing `len`. impl PubAllowed { - pub fn irrelevant(self: &Self) -> bool { + pub fn irrelevant(&self) -> bool { false } } pub trait PubTraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; } impl PubTraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -47,11 +47,11 @@ impl PubTraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -59,11 +59,11 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } @@ -71,7 +71,7 @@ impl HasWrongIsEmpty { struct NotPubOne; impl NotPubOne { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { // No error; `len` is pub but `NotPubOne` is not exported anyway. 1 } @@ -80,19 +80,19 @@ impl NotPubOne { struct One; impl One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { // No error; `len` is private; see issue #1085. 1 } } trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -100,11 +100,11 @@ impl TraitsToo for One { struct HasPrivateIsEmpty; impl HasPrivateIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -112,16 +112,16 @@ impl HasPrivateIsEmpty { struct Wither; pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index 4493b17a4b4..d79c300c074 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -2,7 +2,7 @@ error: item `PubOne` has a public `len` method but no corresponding `is_empty` m --> $DIR/len_without_is_empty.rs:6:1 | LL | / impl PubOne { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } LL | | } @@ -14,7 +14,7 @@ error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_e --> $DIR/len_without_is_empty.rs:37:1 | LL | / pub trait PubTraitsToo { -LL | | fn len(self: &Self) -> isize; +LL | | fn len(&self) -> isize; LL | | } | |_^ @@ -22,7 +22,7 @@ error: item `HasIsEmpty` has a public `len` method but a private `is_empty` meth --> $DIR/len_without_is_empty.rs:49:1 | LL | / impl HasIsEmpty { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } ... | @@ -34,7 +34,7 @@ error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is --> $DIR/len_without_is_empty.rs:61:1 | LL | / impl HasWrongIsEmpty { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } ... | diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index a29b832eb60..d81676a3d9f 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -7,12 +7,12 @@ pub struct One; struct Wither; trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -20,11 +20,11 @@ impl TraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -32,26 +32,26 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 8fd0093f4fd..ecdba921a5d 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -7,12 +7,12 @@ pub struct One; struct Wither; trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -20,11 +20,11 @@ impl TraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -32,26 +32,26 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed new file mode 100644 index 00000000000..642e48fd131 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -0,0 +1,61 @@ +// run-rustfix + +#![warn(clippy::needless_arbitrary_self_type)] +#![allow(unused_mut, clippy::needless_lifetimes)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(&self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(&'a self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn mut_ref_bad(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_bad(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index da4bbcf4a7d..178abc341a8 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -1,4 +1,7 @@ +// run-rustfix + #![warn(clippy::needless_arbitrary_self_type)] +#![allow(unused_mut, clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index ee803b24071..fc21d3d0afd 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -1,5 +1,5 @@ error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:9:16 + --> $DIR/needless_arbitrary_self_type.rs:12:16 | LL | pub fn bad(self: Self) { | ^^^^^^^^^^ help: consider to change this parameter to: `self` @@ -7,37 +7,37 @@ LL | pub fn bad(self: Self) { = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:17:20 + --> $DIR/needless_arbitrary_self_type.rs:20:20 | LL | pub fn mut_bad(mut self: Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:25:20 + --> $DIR/needless_arbitrary_self_type.rs:28:20 | LL | pub fn ref_bad(self: &Self) { | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:29:38 + --> $DIR/needless_arbitrary_self_type.rs:32:38 | LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:37:24 + --> $DIR/needless_arbitrary_self_type.rs:40:24 | LL | pub fn mut_ref_bad(self: &mut Self) { | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:41:42 + --> $DIR/needless_arbitrary_self_type.rs:44:42 | LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:49:28 + --> $DIR/needless_arbitrary_self_type.rs:52:28 | LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 9a0da404cb6..96d1c54946c 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -22,9 +22,9 @@ struct HasOption { } impl HasOption { - fn do_option_nothing(self: &Self, value: usize) {} + fn do_option_nothing(&self, value: usize) {} - fn do_option_plus_one(self: &Self, value: usize) -> usize { + fn do_option_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 58041b62df3..931ffc18665 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -22,9 +22,9 @@ struct HasOption { } impl HasOption { - fn do_option_nothing(self: &Self, value: usize) {} + fn do_option_nothing(&self, value: usize) {} - fn do_option_plus_one(self: &Self, value: usize) -> usize { + fn do_option_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 1d0a3ecd0ff..631042c616b 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -18,9 +18,9 @@ struct HasResult { } impl HasResult { - fn do_result_nothing(self: &Self, value: usize) {} + fn do_result_nothing(&self, value: usize) {} - fn do_result_plus_one(self: &Self, value: usize) -> usize { + fn do_result_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 2fe18f923f0..679eb232626 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -18,9 +18,9 @@ struct HasResult { } impl HasResult { - fn do_result_nothing(self: &Self, value: usize) {} + fn do_result_nothing(&self, value: usize) {} - fn do_result_plus_one(self: &Self, value: usize) -> usize { + fn do_result_plus_one(&self, value: usize) -> usize { value + 1 } } From e03f73e627721c35459886781af281632cac299d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 23:38:55 +0200 Subject: [PATCH 0364/1110] fix nits --- .../src/needless_arbitrary_self_type.rs | 2 +- tests/ui/needless_arbitrary_self_type.fixed | 12 ++++++++-- tests/ui/needless_arbitrary_self_type.rs | 12 ++++++++-- tests/ui/needless_arbitrary_self_type.stderr | 22 +++++++++---------- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 1b23ecd9ad2..4590128bedc 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -82,7 +82,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod cx, NEEDLESS_ARBITRARY_SELF_TYPE, span, - "the type of the `self` parameter is arbitrary", + "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, Applicability::MachineApplicable, diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index 642e48fd131..bc770d8bf68 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -29,11 +29,15 @@ impl ValType { unimplemented!(); } + pub fn ref_good(&self) { + unimplemented!(); + } + pub fn ref_bad_with_lifetime<'a>(&'a self) { unimplemented!(); } - pub fn ref_good(&self) { + pub fn ref_good_with_lifetime<'a>(&'a self) { unimplemented!(); } @@ -41,11 +45,15 @@ impl ValType { unimplemented!(); } + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { unimplemented!(); } - pub fn mut_ref_good(&mut self) { + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 178abc341a8..9074920b204 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -29,11 +29,15 @@ impl ValType { unimplemented!(); } + pub fn ref_good(&self) { + unimplemented!(); + } + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { unimplemented!(); } - pub fn ref_good(&self) { + pub fn ref_good_with_lifetime<'a>(&'a self) { unimplemented!(); } @@ -41,11 +45,15 @@ impl ValType { unimplemented!(); } + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { unimplemented!(); } - pub fn mut_ref_good(&mut self) { + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index fc21d3d0afd..227c6d73b62 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -1,4 +1,4 @@ -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:12:16 | LL | pub fn bad(self: Self) { @@ -6,38 +6,38 @@ LL | pub fn bad(self: Self) { | = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:20:20 | LL | pub fn mut_bad(mut self: Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:28:20 | LL | pub fn ref_bad(self: &Self) { | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:32:38 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:36:38 | LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:40:24 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:44:24 | LL | pub fn mut_ref_bad(self: &mut Self) { | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:44:42 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:52:42 | LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:52:28 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:60:28 | LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` From bfe610cc8d7d380cfaf83f03629a23747fc54fad Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 7 Aug 2020 18:03:12 +0200 Subject: [PATCH 0365/1110] ignore mutable self reference parameters --- clippy_lints/src/needless_arbitrary_self_type.rs | 8 ++++++-- tests/ui/needless_arbitrary_self_type.fixed | 2 +- tests/ui/needless_arbitrary_self_type.rs | 2 +- tests/ui/needless_arbitrary_self_type.stderr | 8 +------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 4590128bedc..38bdd0f7ed2 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -104,8 +104,12 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { } }, TyKind::Rptr(lifetime, mut_ty) => { - if let TyKind::Path(None, path) = &mut_ty.ty.kind { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + if_chain! { + if let TyKind::Path(None, path) = &mut_ty.ty.kind; + if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind; + then { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } } }, _ => {}, diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index bc770d8bf68..9da21eb6b29 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -57,7 +57,7 @@ impl ValType { unimplemented!(); } - pub fn mut_ref_mut_bad(&mut self) { + pub fn mut_ref_mut_good(mut self: &mut Self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 9074920b204..17aeaaf97ac 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -57,7 +57,7 @@ impl ValType { unimplemented!(); } - pub fn mut_ref_mut_bad(mut self: &mut Self) { + pub fn mut_ref_mut_good(mut self: &mut Self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index 227c6d73b62..f4c645d35c8 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -36,11 +36,5 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: the type of the `self` parameter does not need to be arbitrary - --> $DIR/needless_arbitrary_self_type.rs:60:28 - | -LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { - | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors From 87e740921abd4132152f090545fa4c9ed9fa0d6d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 7 Aug 2020 17:55:25 +0200 Subject: [PATCH 0366/1110] check impl Ord / is_float --- clippy_lints/src/minmax.rs | 23 ++++++++++++++++------- tests/ui/min_max.rs | 18 ++++++++++++++++++ tests/ui/min_max.stderr | 32 +++++++++++++++++++------------- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 1f798fd1120..004dd50a31b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -84,12 +85,20 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(ref path, _, ref args, _) => { - if path.ident.as_str() == sym!(max).as_str() { - fetch_const(cx, args, MinMax::Max) - } else if path.ident.as_str() == sym!(min).as_str() { - fetch_const(cx, args, MinMax::Min) - } else { - None + if_chain! { + if let [obj, _] = args; + if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); + then { + if path.ident.as_str() == sym!(max).as_str() { + fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) + } else { + None + } + } else { + None + } } }, _ => None, diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 90ec5676493..f7ed72a11cf 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -6,6 +6,18 @@ use std::cmp::{max, min}; const LARGE: usize = 3; +struct NotOrd(u64); + +impl NotOrd { + fn min(self, x: u64) -> NotOrd { + NotOrd(x) + } + + fn max(self, x: u64) -> NotOrd { + NotOrd(x) + } +} + fn main() { let x; x = 2usize; @@ -31,11 +43,14 @@ fn main() { max("Apple", min(s, "Zoo")); // ok + let f = 3f32; x.min(1).max(3); x.max(3).min(1); + f.max(3f32).min(1f32); x.max(1).min(3); // ok x.min(3).max(1); // ok + f.min(3f32).max(1f32); // ok max(x.min(1), 3); min(x.max(1), 3); // ok @@ -44,4 +59,7 @@ fn main() { s.min("Apple").max("Zoo"); s.min("Zoo").max("Apple"); // ok + + let not_ord = NotOrd(1); + not_ord.min(1).max(3); // ok } diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index 653946dc025..9f8e26fa406 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:12:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,70 +7,76 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:13:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:14:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:15:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:17:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:29:5 + --> $DIR/min_max.rs:41:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:30:5 + --> $DIR/min_max.rs:42:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:34:5 + --> $DIR/min_max.rs:47:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:35:5 + --> $DIR/min_max.rs:48:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:40:5 + --> $DIR/min_max.rs:49:5 + | +LL | f.max(3f32).min(1f32); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:55:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:43:5 + --> $DIR/min_max.rs:58:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:44:5 + --> $DIR/min_max.rs:59:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors From bd71b01a8280dd0faa02416b7064ce170c1b40f5 Mon Sep 17 00:00:00 2001 From: Camelid <37223377+camelid@users.noreply.github.com> Date: Fri, 7 Aug 2020 14:21:14 -0700 Subject: [PATCH 0367/1110] Make the docs clearer for new contributors * Add an easy-to-see note at the top of `CONTRIBUTING.md` that points new contributors to the Basics docs * Add a note about compiler errors as a result of internals changes that break Clippy --- CONTRIBUTING.md | 7 +++++-- doc/basics.md | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfc5cc077c3..5f7b1e85ee9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,11 +28,14 @@ All contributors are expected to follow the [Rust Code of Conduct]. ## Getting started -High level approach: +**Note: If this is your first time contributing to Clippy, you should +first read the [Basics docs](doc/basics.md).** + +### High level approach 1. Find something to fix/improve 2. Change code (likely some file in `clippy_lints/src/`) -3. Follow the instructions in the [Basics docs](doc/basics.md) such as running the `setup-toolchain.sh` script +3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up 4. Run `cargo test` in the root directory and wiggle code until it passes 5. Open a PR (also can be done after 2. if you run into problems) diff --git a/doc/basics.md b/doc/basics.md index 5c07d9b98a5..c81e7f6e069 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -53,6 +53,9 @@ rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools rustup override set master ``` +_Note:_ Sometimes you may get compiler errors when building Clippy, even if you +didn't change anything. Normally those will be fixed by a maintainer in a few hours. + ## Building and Testing Once the `master` toolchain is installed, you can build and test Clippy like From 888657e09a2133fc105382136f61915086144e3f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 8 Aug 2020 18:13:43 +0200 Subject: [PATCH 0368/1110] Fix ICE in `loops` module --- clippy_lints/src/loops.rs | 20 ++++++++++---------- tests/ui/crashes/ice-5872.rs | 5 +++++ tests/ui/crashes/ice-5872.stderr | 10 ++++++++++ tests/ui/needless_collect.fixed | 4 ++-- tests/ui/needless_collect.rs | 2 +- tests/ui/needless_collect.stderr | 4 ++-- 6 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 tests/ui/crashes/ice-5872.rs create mode 100644 tests/ui/crashes/ice-5872.stderr diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6359c20040c..1729fea7bc8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2374,7 +2374,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, @@ -2386,20 +2386,20 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont ); } if method.ident.name == sym!(is_empty) { - let span = shorten_span(expr, sym!(iter)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - "get(0).is_none()".to_string(), + "next().is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2579,13 +2579,13 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) } } -fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { - let mut current_expr = expr; - while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind { - if path.ident.name == target_fn_name { +fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { + if_chain! { + if let ExprKind::MethodCall(.., args, _) = &expr.kind; + if let ExprKind::MethodCall(_, span, ..) = &args[0].kind; + then { return expr.span.with_lo(span.lo()); } - current_expr = &args[0]; } - unreachable!() + unreachable!(); } diff --git a/tests/ui/crashes/ice-5872.rs b/tests/ui/crashes/ice-5872.rs new file mode 100644 index 00000000000..68afa8f8c3a --- /dev/null +++ b/tests/ui/crashes/ice-5872.rs @@ -0,0 +1,5 @@ +#![warn(clippy::needless_collect)] + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); +} diff --git a/tests/ui/crashes/ice-5872.stderr b/tests/ui/crashes/ice-5872.stderr new file mode 100644 index 00000000000..a60ca345cf7 --- /dev/null +++ b/tests/ui/crashes/ice-5872.stderr @@ -0,0 +1,10 @@ +error: avoid using `collect()` when not needed + --> $DIR/ice-5872.rs:4:39 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index be37dc16b9a..7f2fcf02f6b 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -5,11 +5,11 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.get(0).is_none() { + if sample.iter().next().is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 7ee603afeb0..788a9eb3264 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -5,7 +5,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().collect::>().len(); diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 9113aad90dd..2a9539d5975 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -7,10 +7,10 @@ LL | let len = sample.iter().collect::>().len(); = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:15 + --> $DIR/needless_collect.rs:12:22 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:15:28 From fd87cdb357b801b1fab465f0be595386a3f84134 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 8 Aug 2020 19:20:34 +0200 Subject: [PATCH 0369/1110] Run fmt --- clippy_lints/src/functions.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 6a141f1fc78..28b276967bc 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -294,7 +294,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) { + if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) + { check_must_use_candidate( cx, &sig.decl, From a77e881ec9f324cdc544150f897d8b34281f92e4 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 17 Jun 2020 01:16:34 +0200 Subject: [PATCH 0370/1110] should_impl_trait - ignore methods with lifetime params --- clippy_lints/src/methods/mod.rs | 11 ++++++++++- tests/ui/methods.rs | 5 +++++ tests/ui/methods.stderr | 26 +++++++++++++------------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 570ae66d595..3009aa3a64e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1497,11 +1497,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if cx.access_levels.is_exported(impl_item.hir_id) { // check missing trait implementations for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { + let no_lifetime_params = || { + impl_item.generics.params.iter().filter(|p| match p.kind { + hir::GenericParamKind::Lifetime { .. } => true, + _ => false, + }).count() == 0 + }; if name == method_name && sig.decl.inputs.len() == n_args && out_type.matches(cx, &sig.decl.output) && self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) { + fn_header_equals(*fn_header, sig.header) && + // ignore methods with lifetime params, risk of false positive + no_lifetime_params() + { span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( "defining a method called `{}` on this type; consider implementing \ the `{}` trait or choosing a less ambiguous name", name, trait_name)); diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 7880cf36415..3b267b0dab2 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -10,6 +10,7 @@ clippy::non_ascii_literal, clippy::new_without_default, clippy::needless_pass_by_value, + clippy::needless_lifetimes, clippy::print_stdout, clippy::must_use_candidate, clippy::use_self, @@ -82,6 +83,10 @@ impl T { fn new(self) -> Self { unimplemented!(); } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T> { + unimplemented!(); + } } pub struct T1; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 01cf487ac14..9b8ecaed692 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:39:5 + --> $DIR/methods.rs:40:5 | LL | / pub fn add(self, other: T) -> T { LL | | self @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::should-implement-trait` implied by `-D warnings` error: methods called `new` usually return `Self` - --> $DIR/methods.rs:169:5 + --> $DIR/methods.rs:174:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +19,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:188:13 + --> $DIR/methods.rs:193:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:191:13 + --> $DIR/methods.rs:196:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +38,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:208:22 + --> $DIR/methods.rs:213:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +46,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:209:20 + --> $DIR/methods.rs:214:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:210:20 + --> $DIR/methods.rs:215:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:211:22 + --> $DIR/methods.rs:216:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:13 + --> $DIR/methods.rs:219:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +74,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:220:22 + --> $DIR/methods.rs:225:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:223:13 + --> $DIR/methods.rs:228:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +90,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:229:22 + --> $DIR/methods.rs:234:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:232:13 + --> $DIR/methods.rs:237:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ From 2bc0ecd44b4d09476eade641e02451d949a1c8e2 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 19 Jun 2020 22:12:51 +0200 Subject: [PATCH 0371/1110] should_implement_trait - add test cases for every checked trait method --- clippy_lints/src/methods/mod.rs | 1 + tests/ui/methods.rs | 146 ++++++++++++++++-- tests/ui/methods.stderr | 264 ++++++++++++++++++++++++++++++-- 3 files changed, 386 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3009aa3a64e..c225a3bd359 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3424,6 +3424,7 @@ const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30 ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), + // FIXME: default doesn't work ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 3b267b0dab2..adf81607440 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -37,10 +37,137 @@ use option_helpers::IteratorFalsePositives; pub struct T; impl T { + // ******************************************* + // complete trait method list, should lint all + // ******************************************* pub fn add(self, other: T) -> T { - self + unimplemented!() } + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() + } + // ***************** + // complete list end + // ***************** +} + +pub struct T1; +impl T1 { + // corner cases: should not lint + // no error, not public interface pub(crate) fn drop(&mut self) {} @@ -50,22 +177,22 @@ impl T { } // no error, private function - fn eq(&self, other: T) -> bool { + fn eq(&self, other: Self) -> bool { true } // No error; self is a ref. - fn sub(&self, other: T) -> &T { + fn sub(&self, other: Self) -> &Self { self } // No error; different number of arguments. - fn div(self) -> T { + fn div(self) -> Self { self } // No error; wrong return type. - fn rem(self, other: T) {} + fn rem(self, other: Self) {} // Fine fn into_u32(self) -> u32 { @@ -89,16 +216,15 @@ impl T { } } -pub struct T1; - -impl T1 { +pub struct T2; +impl T2 { // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: T1) -> T1 { + pub unsafe fn add(self, rhs: Self) -> Self { self } // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { + pub async fn next(&mut self) -> Option { None } } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 9b8ecaed692..5105fff8f5b 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,15 +1,249 @@ error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:40:5 + --> $DIR/methods.rs:43:5 | LL | / pub fn add(self, other: T) -> T { -LL | | self +LL | | unimplemented!() LL | | } | |_____^ | = note: `-D clippy::should-implement-trait` implied by `-D warnings` +error: defining a method called `as_mut` on this type; consider implementing the `std::convert::AsMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:47:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `as_ref` on this type; consider implementing the `std::convert::AsRef` trait or choosing a less ambiguous name + --> $DIR/methods.rs:51:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitand` on this type; consider implementing the `std::ops::BitAnd` trait or choosing a less ambiguous name + --> $DIR/methods.rs:55:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitor` on this type; consider implementing the `std::ops::BitOr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:59:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitxor` on this type; consider implementing the `std::ops::BitXor` trait or choosing a less ambiguous name + --> $DIR/methods.rs:63:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `borrow` on this type; consider implementing the `std::borrow::Borrow` trait or choosing a less ambiguous name + --> $DIR/methods.rs:67:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `borrow_mut` on this type; consider implementing the `std::borrow::BorrowMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:71:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `clone` on this type; consider implementing the `std::clone::Clone` trait or choosing a less ambiguous name + --> $DIR/methods.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `cmp` on this type; consider implementing the `std::cmp::Ord` trait or choosing a less ambiguous name + --> $DIR/methods.rs:79:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `deref` on this type; consider implementing the `std::ops::Deref` trait or choosing a less ambiguous name + --> $DIR/methods.rs:87:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `deref_mut` on this type; consider implementing the `std::ops::DerefMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:91:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `div` on this type; consider implementing the `std::ops::Div` trait or choosing a less ambiguous name + --> $DIR/methods.rs:95:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `drop` on this type; consider implementing the `std::ops::Drop` trait or choosing a less ambiguous name + --> $DIR/methods.rs:99:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `eq` on this type; consider implementing the `std::cmp::PartialEq` trait or choosing a less ambiguous name + --> $DIR/methods.rs:103:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `from_iter` on this type; consider implementing the `std::iter::FromIterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:107:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `from_str` on this type; consider implementing the `std::str::FromStr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:111:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/methods.rs:111:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::missing-errors-doc` implied by `-D warnings` + +error: defining a method called `hash` on this type; consider implementing the `std::hash::Hash` trait or choosing a less ambiguous name + --> $DIR/methods.rs:115:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `index` on this type; consider implementing the `std::ops::Index` trait or choosing a less ambiguous name + --> $DIR/methods.rs:119:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `index_mut` on this type; consider implementing the `std::ops::IndexMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:123:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:127:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `mul` on this type; consider implementing the `std::ops::Mul` trait or choosing a less ambiguous name + --> $DIR/methods.rs:131:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `neg` on this type; consider implementing the `std::ops::Neg` trait or choosing a less ambiguous name + --> $DIR/methods.rs:135:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `next` on this type; consider implementing the `std::iter::Iterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:139:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `not` on this type; consider implementing the `std::ops::Not` trait or choosing a less ambiguous name + --> $DIR/methods.rs:143:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `rem` on this type; consider implementing the `std::ops::Rem` trait or choosing a less ambiguous name + --> $DIR/methods.rs:147:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `shl` on this type; consider implementing the `std::ops::Shl` trait or choosing a less ambiguous name + --> $DIR/methods.rs:151:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `shr` on this type; consider implementing the `std::ops::Shr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:155:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `sub` on this type; consider implementing the `std::ops::Sub` trait or choosing a less ambiguous name + --> $DIR/methods.rs:159:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + error: methods called `new` usually return `Self` - --> $DIR/methods.rs:174:5 + --> $DIR/methods.rs:300:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +253,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:193:13 + --> $DIR/methods.rs:319:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +262,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:196:13 + --> $DIR/methods.rs:322:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +272,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:213:22 + --> $DIR/methods.rs:339:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +280,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:20 + --> $DIR/methods.rs:340:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:215:20 + --> $DIR/methods.rs:341:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:216:22 + --> $DIR/methods.rs:342:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:219:13 + --> $DIR/methods.rs:345:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +308,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:225:22 + --> $DIR/methods.rs:351:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:228:13 + --> $DIR/methods.rs:354:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +324,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:234:22 + --> $DIR/methods.rs:360:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:237:13 + --> $DIR/methods.rs:363:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -105,5 +339,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 13 previous errors +error: aborting due to 42 previous errors From e6b2254f9e55743dbace44cc73c6447b6bda58e5 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 21 Jun 2020 00:12:09 +0200 Subject: [PATCH 0372/1110] should_implement_trait - pr remarks --- clippy_lints/src/methods/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c225a3bd359..a75989e3f13 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1498,10 +1498,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { // check missing trait implementations for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { let no_lifetime_params = || { - impl_item.generics.params.iter().filter(|p| match p.kind { - hir::GenericParamKind::Lifetime { .. } => true, - _ => false, - }).count() == 0 + !impl_item.generics.params.iter() + .any(|p| matches!( + p.kind, + hir::GenericParamKind::Lifetime { .. })) }; if name == method_name && sig.decl.inputs.len() == n_args && @@ -1510,7 +1510,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn_header_equals(*fn_header, sig.header) && // ignore methods with lifetime params, risk of false positive no_lifetime_params() - { + { span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( "defining a method called `{}` on this type; consider implementing \ the `{}` trait or choosing a less ambiguous name", name, trait_name)); From 7cc1a2ed879e45605a53b802cfa5291c9a51284c Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 23 Jun 2020 21:27:48 +0200 Subject: [PATCH 0373/1110] should_implement_trait - filter on explicit lifetime param only --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a75989e3f13..bad1bb7224e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1501,7 +1501,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { !impl_item.generics.params.iter() .any(|p| matches!( p.kind, - hir::GenericParamKind::Lifetime { .. })) + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) }; if name == method_name && sig.decl.inputs.len() == n_args && From 166c520e9a8b1a45819255e75dee737136aa6ec8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 30 Jul 2020 01:41:12 +0200 Subject: [PATCH 0374/1110] should_impl_trait - pr comments --- clippy_lints/src/methods/mod.rs | 150 ++++++---- tests/ui/methods.rs | 197 +------------ tests/ui/methods.stderr | 270 +----------------- tests/ui/should_impl_trait/corner_cases.rs | 83 ++++++ tests/ui/should_impl_trait/method_list_1.rs | 87 ++++++ .../ui/should_impl_trait/method_list_1.stderr | 143 ++++++++++ tests/ui/should_impl_trait/method_list_2.rs | 88 ++++++ .../ui/should_impl_trait/method_list_2.stderr | 153 ++++++++++ 8 files changed, 670 insertions(+), 501 deletions(-) create mode 100644 tests/ui/should_impl_trait/corner_cases.rs create mode 100644 tests/ui/should_impl_trait/method_list_1.rs create mode 100644 tests/ui/should_impl_trait/method_list_1.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.rs create mode 100644 tests/ui/should_impl_trait/method_list_2.stderr diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bad1bb7224e..a549c3b78ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1495,25 +1495,31 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { if cx.access_levels.is_exported(impl_item.hir_id) { - // check missing trait implementations - for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { - let no_lifetime_params = || { - !impl_item.generics.params.iter() - .any(|p| matches!( - p.kind, - hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) - }; - if name == method_name && - sig.decl.inputs.len() == n_args && - out_type.matches(cx, &sig.decl.output) && - self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) && - // ignore methods with lifetime params, risk of false positive - no_lifetime_params() + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name && + sig.decl.inputs.len() == method_config.param_count && + method_config.output_type.matches(cx, &sig.decl.output) && + method_config.self_kind.matches(cx, self_ty, first_arg_ty) && + fn_header_equals(*method_config.fn_header, sig.header) && + method_config.lifetime_param_cond(&impl_item) { - span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( - "defining a method called `{}` on this type; consider implementing \ - the `{}` trait or choosing a less ambiguous name", name, trait_name)); + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, + method_config.trait_name, + method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ) + ); } } } @@ -3412,39 +3418,85 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { abi: rustc_target::spec::abi::Abi::Rust, }; +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: &'static hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: &'static hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + #[rustfmt::skip] -const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [ - ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"), - ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), - ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), - ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), - ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"), - ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"), - ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), - ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), - ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), - ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", "add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, true), // FIXME: default doesn't work - ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), - ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), - ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), - ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"), - ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), - ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), - ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"), - ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"), - ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), - ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"), - ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), - ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), - ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"), - ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"), - ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), - ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"), - ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"), - ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"), - ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"), - ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"), + ShouldImplTraitCase::new("std::default::Default", "default", 0, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index adf81607440..80dd2f744b3 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -34,201 +34,6 @@ use std::sync::{self, Arc}; use option_helpers::IteratorFalsePositives; -pub struct T; - -impl T { - // ******************************************* - // complete trait method list, should lint all - // ******************************************* - pub fn add(self, other: T) -> T { - unimplemented!() - } - - pub fn as_mut(&mut self) -> &mut T { - unimplemented!() - } - - pub fn as_ref(&self) -> &T { - unimplemented!() - } - - pub fn bitand(self, rhs: T) -> T { - unimplemented!() - } - - pub fn bitor(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn bitxor(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn borrow(&self) -> &str { - unimplemented!() - } - - pub fn borrow_mut(&mut self) -> &mut str { - unimplemented!() - } - - pub fn clone(&self) -> Self { - unimplemented!() - } - - pub fn cmp(&self, other: &Self) -> Self { - unimplemented!() - } - - pub fn default() -> Self { - unimplemented!() - } - - pub fn deref(&self) -> &Self { - unimplemented!() - } - - pub fn deref_mut(&mut self) -> &mut Self { - unimplemented!() - } - - pub fn div(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn drop(&mut self) { - unimplemented!() - } - - pub fn eq(&self, other: &Self) -> bool { - unimplemented!() - } - - pub fn from_iter(iter: T) -> Self { - unimplemented!() - } - - pub fn from_str(s: &str) -> Result { - unimplemented!() - } - - pub fn hash(&self, state: &mut T) { - unimplemented!() - } - - pub fn index(&self, index: usize) -> &Self { - unimplemented!() - } - - pub fn index_mut(&mut self, index: usize) -> &mut Self { - unimplemented!() - } - - pub fn into_iter(self) -> Self { - unimplemented!() - } - - pub fn mul(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn neg(self) -> Self { - unimplemented!() - } - - pub fn next(&mut self) -> Option { - unimplemented!() - } - - pub fn not(self) -> Self { - unimplemented!() - } - - pub fn rem(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn shl(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn shr(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn sub(self, rhs: Self) -> Self { - unimplemented!() - } - // ***************** - // complete list end - // ***************** -} - -pub struct T1; -impl T1 { - // corner cases: should not lint - - // no error, not public interface - pub(crate) fn drop(&mut self) {} - - // no error, private function - fn neg(self) -> Self { - self - } - - // no error, private function - fn eq(&self, other: Self) -> bool { - true - } - - // No error; self is a ref. - fn sub(&self, other: Self) -> &Self { - self - } - - // No error; different number of arguments. - fn div(self) -> Self { - self - } - - // No error; wrong return type. - fn rem(self, other: Self) {} - - // Fine - fn into_u32(self) -> u32 { - 0 - } - - fn into_u16(&self) -> u16 { - 0 - } - - fn to_something(self) -> u32 { - 0 - } - - fn new(self) -> Self { - unimplemented!(); - } - - pub fn next<'b>(&'b mut self) -> Option<&'b mut T> { - unimplemented!(); - } -} - -pub struct T2; -impl T2 { - // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: Self) -> Self { - self - } - - // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { - None - } -} - struct Lt<'a> { foo: &'a u32, } @@ -302,6 +107,8 @@ impl BadNew { } } +struct T; + impl Mul for T { type Output = T; // No error, obviously. diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 5105fff8f5b..2a0a43e83a6 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,249 +1,5 @@ -error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:43:5 - | -LL | / pub fn add(self, other: T) -> T { -LL | | unimplemented!() -LL | | } - | |_____^ - | - = note: `-D clippy::should-implement-trait` implied by `-D warnings` - -error: defining a method called `as_mut` on this type; consider implementing the `std::convert::AsMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:47:5 - | -LL | / pub fn as_mut(&mut self) -> &mut T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `as_ref` on this type; consider implementing the `std::convert::AsRef` trait or choosing a less ambiguous name - --> $DIR/methods.rs:51:5 - | -LL | / pub fn as_ref(&self) -> &T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitand` on this type; consider implementing the `std::ops::BitAnd` trait or choosing a less ambiguous name - --> $DIR/methods.rs:55:5 - | -LL | / pub fn bitand(self, rhs: T) -> T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitor` on this type; consider implementing the `std::ops::BitOr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:59:5 - | -LL | / pub fn bitor(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitxor` on this type; consider implementing the `std::ops::BitXor` trait or choosing a less ambiguous name - --> $DIR/methods.rs:63:5 - | -LL | / pub fn bitxor(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `borrow` on this type; consider implementing the `std::borrow::Borrow` trait or choosing a less ambiguous name - --> $DIR/methods.rs:67:5 - | -LL | / pub fn borrow(&self) -> &str { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `borrow_mut` on this type; consider implementing the `std::borrow::BorrowMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:71:5 - | -LL | / pub fn borrow_mut(&mut self) -> &mut str { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `clone` on this type; consider implementing the `std::clone::Clone` trait or choosing a less ambiguous name - --> $DIR/methods.rs:75:5 - | -LL | / pub fn clone(&self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `cmp` on this type; consider implementing the `std::cmp::Ord` trait or choosing a less ambiguous name - --> $DIR/methods.rs:79:5 - | -LL | / pub fn cmp(&self, other: &Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `deref` on this type; consider implementing the `std::ops::Deref` trait or choosing a less ambiguous name - --> $DIR/methods.rs:87:5 - | -LL | / pub fn deref(&self) -> &Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `deref_mut` on this type; consider implementing the `std::ops::DerefMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:91:5 - | -LL | / pub fn deref_mut(&mut self) -> &mut Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `div` on this type; consider implementing the `std::ops::Div` trait or choosing a less ambiguous name - --> $DIR/methods.rs:95:5 - | -LL | / pub fn div(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `drop` on this type; consider implementing the `std::ops::Drop` trait or choosing a less ambiguous name - --> $DIR/methods.rs:99:5 - | -LL | / pub fn drop(&mut self) { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `eq` on this type; consider implementing the `std::cmp::PartialEq` trait or choosing a less ambiguous name - --> $DIR/methods.rs:103:5 - | -LL | / pub fn eq(&self, other: &Self) -> bool { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `from_iter` on this type; consider implementing the `std::iter::FromIterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:107:5 - | -LL | / pub fn from_iter(iter: T) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `from_str` on this type; consider implementing the `std::str::FromStr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:111:5 - | -LL | / pub fn from_str(s: &str) -> Result { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: docs for function returning `Result` missing `# Errors` section - --> $DIR/methods.rs:111:5 - | -LL | / pub fn from_str(s: &str) -> Result { -LL | | unimplemented!() -LL | | } - | |_____^ - | - = note: `-D clippy::missing-errors-doc` implied by `-D warnings` - -error: defining a method called `hash` on this type; consider implementing the `std::hash::Hash` trait or choosing a less ambiguous name - --> $DIR/methods.rs:115:5 - | -LL | / pub fn hash(&self, state: &mut T) { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `index` on this type; consider implementing the `std::ops::Index` trait or choosing a less ambiguous name - --> $DIR/methods.rs:119:5 - | -LL | / pub fn index(&self, index: usize) -> &Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `index_mut` on this type; consider implementing the `std::ops::IndexMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:123:5 - | -LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:127:5 - | -LL | / pub fn into_iter(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `mul` on this type; consider implementing the `std::ops::Mul` trait or choosing a less ambiguous name - --> $DIR/methods.rs:131:5 - | -LL | / pub fn mul(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `neg` on this type; consider implementing the `std::ops::Neg` trait or choosing a less ambiguous name - --> $DIR/methods.rs:135:5 - | -LL | / pub fn neg(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `next` on this type; consider implementing the `std::iter::Iterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:139:5 - | -LL | / pub fn next(&mut self) -> Option { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `not` on this type; consider implementing the `std::ops::Not` trait or choosing a less ambiguous name - --> $DIR/methods.rs:143:5 - | -LL | / pub fn not(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `rem` on this type; consider implementing the `std::ops::Rem` trait or choosing a less ambiguous name - --> $DIR/methods.rs:147:5 - | -LL | / pub fn rem(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `shl` on this type; consider implementing the `std::ops::Shl` trait or choosing a less ambiguous name - --> $DIR/methods.rs:151:5 - | -LL | / pub fn shl(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `shr` on this type; consider implementing the `std::ops::Shr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:155:5 - | -LL | / pub fn shr(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `sub` on this type; consider implementing the `std::ops::Sub` trait or choosing a less ambiguous name - --> $DIR/methods.rs:159:5 - | -LL | / pub fn sub(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - error: methods called `new` usually return `Self` - --> $DIR/methods.rs:300:5 + --> $DIR/methods.rs:105:5 | LL | / fn new() -> i32 { LL | | 0 @@ -253,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:319:13 + --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -262,7 +18,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:322:13 + --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -272,7 +28,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:339:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -280,25 +36,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:340:20 + --> $DIR/methods.rs:147:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:341:20 + --> $DIR/methods.rs:148:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:342:22 + --> $DIR/methods.rs:149:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:345:13 + --> $DIR/methods.rs:152:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -308,13 +64,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:351:22 + --> $DIR/methods.rs:158:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:354:13 + --> $DIR/methods.rs:161:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -324,13 +80,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:360:22 + --> $DIR/methods.rs:167:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:363:13 + --> $DIR/methods.rs:170:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -339,5 +95,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 42 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/should_impl_trait/corner_cases.rs b/tests/ui/should_impl_trait/corner_cases.rs new file mode 100644 index 00000000000..6c5ffe6aba8 --- /dev/null +++ b/tests/ui/should_impl_trait/corner_cases.rs @@ -0,0 +1,83 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +pub struct T1; +impl T1 { + // corner cases: should not lint + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: Self) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: Self) -> &Self { + self + } + + // No error; different number of arguments. + fn div(self) -> Self { + self + } + + // No error; wrong return type. + fn rem(self, other: Self) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> { + unimplemented!(); + } +} + +pub struct T2; +impl T2 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: Self) -> Self { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option { + None + } +} diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs new file mode 100644 index 00000000000..f8d248fc98d --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -0,0 +1,87 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 1, should lint all + // ***************************************** + pub fn add(self, other: T) -> T { + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + // ********** + // part 1 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr new file mode 100644 index 00000000000..2b7d4628c3f --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -0,0 +1,143 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> $DIR/method_list_1.rs:25:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> $DIR/method_list_1.rs:29:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> $DIR/method_list_1.rs:33:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> $DIR/method_list_1.rs:37:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> $DIR/method_list_1.rs:41:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> $DIR/method_list_1.rs:45:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> $DIR/method_list_1.rs:49:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> $DIR/method_list_1.rs:53:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> $DIR/method_list_1.rs:57:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> $DIR/method_list_1.rs:61:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> $DIR/method_list_1.rs:69:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> $DIR/method_list_1.rs:73:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> $DIR/method_list_1.rs:77:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> $DIR/method_list_1.rs:81:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs new file mode 100644 index 00000000000..ed5e0d384bf --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -0,0 +1,88 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 2, should lint all + // ***************************************** + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() + } + // ********** + // part 2 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr new file mode 100644 index 00000000000..b6fd4356956 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -0,0 +1,153 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> $DIR/method_list_2.rs:26:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> $DIR/method_list_2.rs:30:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> $DIR/method_list_2.rs:34:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> $DIR/method_list_2.rs:38:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> $DIR/method_list_2.rs:42:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> $DIR/method_list_2.rs:46:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> $DIR/method_list_2.rs:50:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> $DIR/method_list_2.rs:54:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> $DIR/method_list_2.rs:58:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> $DIR/method_list_2.rs:62:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> $DIR/method_list_2.rs:66:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> $DIR/method_list_2.rs:70:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> $DIR/method_list_2.rs:74:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> $DIR/method_list_2.rs:78:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> $DIR/method_list_2.rs:82:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + From f9ba829f6701ae03a5c226044dbbde13ce87e123 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 9 Aug 2020 15:35:41 +0200 Subject: [PATCH 0375/1110] should_impl_trait - self linting --- clippy_lints/src/methods/mod.rs | 67 +++++++++++++++++---------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a549c3b78ef..07e55ab0762 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1470,6 +1470,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if in_external_macro(cx.sess(), impl_item.span) { return; @@ -1501,7 +1502,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { sig.decl.inputs.len() == method_config.param_count && method_config.output_type.matches(cx, &sig.decl.output) && method_config.self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*method_config.fn_header, sig.header) && + fn_header_equals(method_config.fn_header, sig.header) && method_config.lifetime_param_cond(&impl_item) { span_lint_and_help( @@ -3422,7 +3423,7 @@ struct ShouldImplTraitCase { trait_name: &'static str, method_name: &'static str, param_count: usize, - fn_header: &'static hir::FnHeader, + fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) self_kind: SelfKind, // checks against the output type @@ -3435,7 +3436,7 @@ impl ShouldImplTraitCase { trait_name: &'static str, method_name: &'static str, param_count: usize, - fn_header: &'static hir::FnHeader, + fn_header: hir::FnHeader, self_kind: SelfKind, output_type: OutType, lint_explicit_lifetime: bool, @@ -3466,37 +3467,37 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", "add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), // FIXME: default doesn't work - ShouldImplTraitCase::new("std::default::Default", "default", 0, &FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", "div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", "index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", "not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] From 6af969379e766bf85652196c04ff267edf5cace7 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sun, 9 Aug 2020 22:21:09 +0500 Subject: [PATCH 0376/1110] Prevent compile parts of rustc when using `cargo dev ra-setup` --- clippy_dev/src/ra_setup.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index 8617445c8a6..f2bd651ab25 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -68,10 +68,11 @@ fn inject_deps_into_manifest( }); // format a new [dependencies]-block with the new deps we need to inject - let mut all_deps = String::from("[dependencies]\n"); + let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n"); new_deps.for_each(|dep_line| { all_deps.push_str(&dep_line); }); + all_deps.push_str("\n[dependencies]\n"); // replace "[dependencies]" with // [dependencies] From 3e3e50bf0fa6282c7265e34589170033c2301edd Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 08:50:20 -0600 Subject: [PATCH 0377/1110] Add example of false positive to PTR_ARG docs. Fixes #214 --- clippy_lints/src/ptr.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7b6bd69ffca..33c1bbd488a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -36,14 +36,27 @@ declare_clippy_lint! { /// argument may also fail to compile if you change the argument. Applying /// this lint on them will fix the problem, but they may be in other crates. /// + /// One notable example of a function that may cause issues, and which cannot + /// easily be changed due to beinng in the standard library is `Vec::contains`. + /// when called on a `Vec>`. If a `&Vec` is passed to that method then + /// it will compile, but if a `&[T]` is passed then it will not compile. + /// + /// ```ignore + /// fn cannot_take_a_slice(v: &Vec) -> bool { + /// let vec_of_vecs: Vec> = some_other_fn(); + /// + /// vec_of_vecs.contains(v) + /// } + /// ``` + /// /// Also there may be `fn(&Vec)`-typed references pointing to your function. /// If you have them, you will get a compiler error after applying this lint's /// suggestions. You then have the choice to undo your changes or change the /// type of the reference. /// /// Note that if the function is part of your public interface, there may be - /// other crates referencing it you may not be aware. Carefully deprecate the - /// function before applying the lint suggestions in this case. + /// other crates referencing it, of which you may not be aware. Carefully + /// deprecate the function before applying the lint suggestions in this case. /// /// **Example:** /// ```ignore From fbf637d12c95528846bfa65ce67bd652f2affb43 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 09:19:40 -0600 Subject: [PATCH 0378/1110] formatting --- clippy_lints/src/ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 33c1bbd488a..7f58a381adc 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -55,7 +55,7 @@ declare_clippy_lint! { /// type of the reference. /// /// Note that if the function is part of your public interface, there may be - /// other crates referencing it, of which you may not be aware. Carefully + /// other crates referencing it, of which you may not be aware. Carefully /// deprecate the function before applying the lint suggestions in this case. /// /// **Example:** From 82c816ee022c420306c6a7475162f252a8432c30 Mon Sep 17 00:00:00 2001 From: robojumper Date: Mon, 10 Aug 2020 17:45:04 +0200 Subject: [PATCH 0379/1110] Fix CHANGELOG's commit range links Two most recent links linked to the wrong range changelog: none --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f09af0466c0..9d957371c6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,13 @@ document. ## Unreleased / In Rust Nightly -[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) +[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master) ## Rust 1.46 Current beta, release 2020-08-27 -[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) ### New lints From e57aafe33f338208e612ecb816a948d28f2c3741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 15:06:33 +0200 Subject: [PATCH 0380/1110] too-many-lines: make lint adhere to lint message convention --- clippy_lints/src/functions.rs | 2 +- tests/ui-toml/functions_maxlines/test.stderr | 4 ++-- tests/ui/functions_maxlines.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 28b276967bc..ac1c7aa9bbb 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -374,7 +374,7 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.") + span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines") } } diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index 4b77ac551e7..fb12257021a 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { diff --git a/tests/ui/functions_maxlines.stderr b/tests/ui/functions_maxlines.stderr index 9b0e7550cc3..c640c82d6d7 100644 --- a/tests/ui/functions_maxlines.stderr +++ b/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { From 0876f17d77e8747f4cba889bd29fb64a0dc1a63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 15:14:12 +0200 Subject: [PATCH 0381/1110] bool-comparison: make lint adhere to lint message convention --- clippy_lints/src/needless_bool.rs | 2 +- tests/ui/bool_comparison.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 8e44f2ec240..dc5aa669139 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>( cx, BOOL_COMPARISON, e.span, - "This comparison might be written more concisely", + "this comparison might be written more concisely", "try simplifying it as shown", format!( "{} != {}", diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index eeb1f20ee89..55d94b8257d 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:120:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:121:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:125:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:126:8 | LL | if !b == a {}; From fd379a889e25c94c0568fe6fc08d0783bcbc3dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 15:16:10 +0200 Subject: [PATCH 0382/1110] builtin-type-shadow: make lint adhere to lint message convention --- clippy_lints/src/misc_early.rs | 2 +- tests/ui/builtin-type-shadow.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 29aba7c1218..38b11c5e733 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -271,7 +271,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, BUILTIN_TYPE_SHADOW, param.ident.span, - &format!("This generic shadows the built-in type `{}`", name), + &format!("this generic shadows the built-in type `{}`", name), ); } } diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr index bc785b075e0..f42b246afd2 100644 --- a/tests/ui/builtin-type-shadow.stderr +++ b/tests/ui/builtin-type-shadow.stderr @@ -1,4 +1,4 @@ -error: This generic shadows the built-in type `u32` +error: this generic shadows the built-in type `u32` --> $DIR/builtin-type-shadow.rs:4:8 | LL | fn foo(a: u32) -> u32 { From 40416c0fa8409da63fb27f065e82cabb51ec17d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 15:18:13 +0200 Subject: [PATCH 0383/1110] naive_bytecount: make lint adhere to lint message convention --- clippy_lints/src/bytecount.rs | 4 ++-- tests/ui/bytecount.stderr | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index dde799fcae4..cdb49d777d8 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { cx, NAIVE_BYTECOUNT, expr.span, - "You appear to be counting bytes the naive way", - "Consider using the bytecount crate", + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", format!("bytecount::count({}, {})", snippet_with_applicability(cx, haystack.span, "..", &mut applicability), snippet_with_applicability(cx, needle.span, "..", &mut applicability)), diff --git a/tests/ui/bytecount.stderr b/tests/ui/bytecount.stderr index 436f5d86a06..1dc37fc8b25 100644 --- a/tests/ui/bytecount.stderr +++ b/tests/ui/bytecount.stderr @@ -1,8 +1,8 @@ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:5:13 | LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)` | note: the lint level is defined here --> $DIR/bytecount.rs:1:8 @@ -10,17 +10,17 @@ note: the lint level is defined here LL | #[deny(clippy::naive_bytecount)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:7:13 | LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)` -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:19:13 | LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)` error: aborting due to 3 previous errors From 5d66bd7bb3fd701d70ec11217e3f89fabe5cb0a7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 10 Aug 2020 23:38:58 +0200 Subject: [PATCH 0384/1110] Avoid or_fun_call for const_fn with no args --- clippy_lints/src/utils/mod.rs | 9 +++++++++ tests/ui/or_fun_call.fixed | 8 ++++++++ tests/ui/or_fun_call.rs | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 9f967d59c8b..223628cc610 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -43,6 +43,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -868,11 +869,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { + cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() + } + if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 + def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + const_eval::is_const_fn(cx.tcx, def_id) + }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2045ffdb5f0..67faa8bd4a0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -116,4 +116,12 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 522f31b72d0..9867e2eedcf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -116,4 +116,12 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); +} + fn main() {} From 9b7ab1d38b13ad8af555793cdf7ce08c12c22595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 16:58:20 +0200 Subject: [PATCH 0385/1110] checked-conversions: make lint adhere to lint message convention --- clippy_lints/src/checked_conversions.rs | 2 +- tests/ui/checked_conversions.stderr | 32 ++++++++++++------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 841902943f0..28c1a54d2c5 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { cx, CHECKED_CONVERSIONS, item.span, - "Checked cast can be simplified.", + "checked cast can be simplified", "try", format!("{}::try_from({}).is_ok()", to_type, snippet), applicability, diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 648ba3ccd01..18518def0ac 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,4 +1,4 @@ -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; @@ -6,91 +6,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | = note: `-D clippy::checked-conversions` implied by `-D warnings` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; From 8679dd375b928a3a5d8420401ed80057eea6f198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 17:06:29 +0200 Subject: [PATCH 0386/1110] unnecessary_unwrap, panicking_unwrap: make lints adhere to lint message convention --- clippy_lints/src/unwrap.rs | 6 +-- .../complex_conditionals.stderr | 40 +++++++++---------- .../complex_conditionals_nested.stderr | 4 +- .../checked_unwrap/simple_conditionals.stderr | 26 ++++++------ 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index f2bbde28c2a..fd755dcc790 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("You checked before that `{}()` cannot fail. \ - Instead of checking and unwrapping, it's better to use `if let` or `match`.", + &format!("you checked before that `{}()` cannot fail. \ + Instead of checking and unwrapping, it's better to use `if let` or `match`", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, ); @@ -191,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, PANICKING_UNWRAP, expr.span, - &format!("This call to `{}()` will always panic.", + &format!("this call to `{}()` will always panic", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, ); diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index dc666bab460..5b62dca629f 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:9:9 | LL | if x.is_ok() && y.is_err() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:10:9 | LL | if x.is_ok() && y.is_err() { @@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -45,7 +45,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:25:9 | LL | if x.is_ok() || y.is_ok() { @@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -63,7 +63,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:27:9 | LL | if x.is_ok() || y.is_ok() { @@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -89,7 +89,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:33:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -98,7 +98,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:34:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -125,7 +125,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:37:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -134,7 +134,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:45:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -161,7 +161,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:48:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -170,7 +170,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:49:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index e4d085470c3..46ffc16c23e 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals_nested.rs:10:13 | LL | if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 4013d1ed667..bf4b6c93098 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:41:9 | LL | if x.is_some() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:44:9 | LL | if x.is_none() { @@ -35,7 +35,7 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { @@ -44,7 +44,7 @@ LL | if x.is_none() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { @@ -57,7 +57,7 @@ LL | m!(x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { @@ -65,7 +65,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:55:9 | LL | if x.is_ok() { @@ -74,7 +74,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { @@ -83,7 +83,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { @@ -92,7 +92,7 @@ LL | if x.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:61:9 | LL | if x.is_err() { @@ -100,7 +100,7 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { @@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { @@ -118,7 +118,7 @@ LL | if x.is_err() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { From 3d592b515492c8d054583078163c1986c44e222a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 19:21:31 +0200 Subject: [PATCH 0387/1110] cmp_null: make lint adhere to lint message convention --- clippy_lints/src/ptr.rs | 2 +- tests/ui/cmp_null.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7b6bd69ffca..460d631fab0 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { cx, CMP_NULL, expr.span, - "Comparing with null is better expressed by the `.is_null()` method", + "comparing with null is better expressed by the `.is_null()` method", ); } } diff --git a/tests/ui/cmp_null.stderr b/tests/ui/cmp_null.stderr index b563a2ebec2..a1f4c70fb27 100644 --- a/tests/ui/cmp_null.stderr +++ b/tests/ui/cmp_null.stderr @@ -1,4 +1,4 @@ -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:9:8 | LL | if p == ptr::null() { @@ -6,7 +6,7 @@ LL | if p == ptr::null() { | = note: `-D clippy::cmp-null` implied by `-D warnings` -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:14:8 | LL | if m == ptr::null_mut() { From 6b0a6a70f8dfa743707c440fdf908b9aceb1b8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 19:39:35 +0200 Subject: [PATCH 0388/1110] default-trait-access: make lint adhere to lint message convention --- clippy_lints/src/default_trait_access.rs | 2 +- tests/ui/default_trait_access.stderr | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index ea244768129..874e19d9e9f 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("Calling `{}` is more clear than this expression", replacement), + &format!("calling `{}` is more clear than this expression", replacement), "try", replacement, Applicability::Unspecified, // First resolve the TODO above diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 453760c6b92..26b2057548b 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,4 +1,4 @@ -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:8:22 | LL | let s1: String = Default::default(); @@ -6,43 +6,43 @@ LL | let s1: String = Default::default(); | = note: `-D clippy::default-trait-access` implied by `-D warnings` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:12:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:14:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:18:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `GenericDerivedDefault::default()` is more clear than this expression +error: calling `GenericDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:28:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` -error: Calling `TupleDerivedDefault::default()` is more clear than this expression +error: calling `TupleDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:34:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` -error: Calling `ArrayDerivedDefault::default()` is more clear than this expression +error: calling `ArrayDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:36:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` -error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression +error: calling `TupleStructDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:40:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); From ba7a01a6a8709eaff32f88d47382c940b24a3c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 19:50:28 +0200 Subject: [PATCH 0389/1110] double-comparisons: make lint adhere to lint message convention --- clippy_lints/src/double_comparison.rs | 2 +- tests/ui/double_comparison.stderr | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 5d16192b754..bae7c4647d4 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -60,7 +60,7 @@ impl<'tcx> DoubleComparisons { cx, DOUBLE_COMPARISONS, span, - "This binary expression can be simplified", + "this binary expression can be simplified", "try", sugg, applicability, diff --git a/tests/ui/double_comparison.stderr b/tests/ui/double_comparison.stderr index 5dcda7b3af4..05ef4e25f7f 100644 --- a/tests/ui/double_comparison.stderr +++ b/tests/ui/double_comparison.stderr @@ -1,4 +1,4 @@ -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:6:8 | LL | if x == y || x < y { @@ -6,43 +6,43 @@ LL | if x == y || x < y { | = note: `-D clippy::double-comparisons` implied by `-D warnings` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:9:8 | LL | if x < y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x <= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:12:8 | LL | if x == y || x > y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:15:8 | LL | if x > y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:18:8 | LL | if x < y || x > y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:21:8 | LL | if x > y || x < y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:24:8 | LL | if x <= y && x >= y { | ^^^^^^^^^^^^^^^^ help: try: `x == y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:27:8 | LL | if x >= y && x <= y { From 590b91d8d4250bd6e30e2396a36c54cdbae3780f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 19:56:01 +0200 Subject: [PATCH 0390/1110] double-parens: make lint adhere to lint message convention and do minor refactoring --- clippy_lints/src/double_parens.rs | 23 +++++------------------ tests/ui/double_parens.stderr | 12 ++++++------ 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 1eb380a22cc..abbcaf43f41 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -45,15 +45,12 @@ impl EarlyLintPass for DoubleParens { return; } + let msg: &str = "consider removing unnecessary double parentheses"; + match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint( - cx, - DOUBLE_PARENS, - expr.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, expr.span, &msg); }, _ => {}, }, @@ -61,12 +58,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, @@ -74,12 +66,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index 0e4c9b5682d..40fcad2ab1d 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,4 +1,4 @@ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:15:5 | LL | ((0)) @@ -6,31 +6,31 @@ LL | ((0)) | = note: `-D clippy::double-parens` implied by `-D warnings` -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:19:14 | LL | dummy_fn((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:23:20 | LL | x.dummy_method((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:27:5 | LL | ((1, 2)) | ^^^^^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:31:5 | LL | (()) | ^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:53:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); From 0db5cb13934bff7a561dde2e13c3455120dc1bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 20:05:42 +0200 Subject: [PATCH 0391/1110] drop_bounds: make lint adhere to lint message convention --- clippy_lints/src/drop_bounds.rs | 6 +++--- tests/ui/drop_bounds.stderr | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index 4afbd1ed0e5..ec3b6afa630 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -33,11 +33,11 @@ declare_clippy_lint! { /// ``` pub DROP_BOUNDS, correctness, - "Bounds of the form `T: Drop` are useless" + "bounds of the form `T: Drop` are useless" } -const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \ - Use `std::mem::needs_drop` to detect if a type has drop glue."; +const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \ + use `std::mem::needs_drop` to detect if a type has drop glue"; declare_lint_pass!(DropBounds => [DROP_BOUNDS]); diff --git a/tests/ui/drop_bounds.stderr b/tests/ui/drop_bounds.stderr index 5d360ef30a1..8208c0ed7e3 100644 --- a/tests/ui/drop_bounds.stderr +++ b/tests/ui/drop_bounds.stderr @@ -1,4 +1,4 @@ -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:2:11 | LL | fn foo() {} @@ -6,7 +6,7 @@ LL | fn foo() {} | = note: `#[deny(clippy::drop_bounds)]` on by default -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:5:8 | LL | T: Drop, From 2792260636f720a44921c5f6571535e887aa6047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 22:40:50 +0200 Subject: [PATCH 0392/1110] empty-liner-after-outer-attr: make lint adhere to lint message convention --- clippy_lints/src/attrs.rs | 2 +- tests/ui/empty_line_after_outer_attribute.stderr | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3ce110e8e0f..376ac55f9c9 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -605,7 +605,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as cx, EMPTY_LINE_AFTER_OUTER_ATTR, begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ + "found an empty line after an outer attribute. \ Perhaps you forgot to add a `!` to make it an inner attribute?", ); } diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index bf753a732f0..594fca44a32 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,4 +1,4 @@ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] @@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } | = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] @@ -17,7 +17,7 @@ LL | | LL | | fn with_one_newline() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] @@ -26,7 +26,7 @@ LL | | LL | | fn with_two_newlines() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] @@ -34,7 +34,7 @@ LL | | LL | | enum Baz { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] @@ -42,7 +42,7 @@ LL | | LL | | struct Foo { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] From 4418ff122fdc65d642dd4adb709634c4f879171e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 23:31:33 +0200 Subject: [PATCH 0393/1110] unneeded-field-pattern: make lint adhere to lint message convention --- clippy_lints/src/misc_early.rs | 10 +++++----- tests/ui/unneeded_field_pattern.stderr | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 38b11c5e733..02789735c17 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -298,9 +298,9 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, pat.span, - "All the struct fields are matched to a wildcard pattern, consider using `..`.", + "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("Try with `{} {{ .. }}` instead", type_name), + &format!("try with `{} {{ .. }}` instead", type_name), ); return; } @@ -313,7 +313,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` instead", + "you matched a field with a wildcard pattern, consider using `..` instead", ); } else { let mut normal = vec![]; @@ -333,10 +333,10 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` \ + "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), ); } } diff --git a/tests/ui/unneeded_field_pattern.stderr b/tests/ui/unneeded_field_pattern.stderr index e7b92ce1e19..b8d3c294532 100644 --- a/tests/ui/unneeded_field_pattern.stderr +++ b/tests/ui/unneeded_field_pattern.stderr @@ -1,19 +1,19 @@ -error: You matched a field with a wildcard pattern. Consider using `..` instead +error: you matched a field with a wildcard pattern, consider using `..` instead --> $DIR/unneeded_field_pattern.rs:14:15 | LL | Foo { a: _, b: 0, .. } => {}, | ^^^^ | = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings` - = help: Try with `Foo { b: 0, .. }` + = help: try with `Foo { b: 0, .. }` -error: All the struct fields are matched to a wildcard pattern, consider using `..`. +error: all the struct fields are matched to a wildcard pattern, consider using `..` --> $DIR/unneeded_field_pattern.rs:16:9 | LL | Foo { a: _, b: _, c: _ } => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Try with `Foo { .. }` instead + = help: try with `Foo { .. }` instead error: aborting due to 2 previous errors From b36a6c9594ffb7e225a3d8872d8de2889ea0bac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 23:36:20 +0200 Subject: [PATCH 0394/1110] ref_in_deref: make lint adhere to lint message convention --- clippy_lints/src/reference.rs | 2 +- tests/ui/unnecessary_ref.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index fe457aad50e..3fda00403c6 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for RefInDeref { cx, REF_IN_DEREF, object.span, - "Creating a reference that is immediately dereferenced.", + "creating a reference that is immediately dereferenced", "try this", snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), applicability, diff --git a/tests/ui/unnecessary_ref.stderr b/tests/ui/unnecessary_ref.stderr index 34ba167a947..d0a0f219097 100644 --- a/tests/ui/unnecessary_ref.stderr +++ b/tests/ui/unnecessary_ref.stderr @@ -1,4 +1,4 @@ -error: Creating a reference that is immediately dereferenced. +error: creating a reference that is immediately dereferenced --> $DIR/unnecessary_ref.rs:13:17 | LL | let inner = (&outer).inner; From 7954c22a99275a8b7be79c14d2bb882750de53ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 23:37:16 +0200 Subject: [PATCH 0395/1110] unknown: make lint adhere to lint message convention --- clippy_lints/src/utils/attrs.rs | 2 +- tests/ui/unknown_attribute.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 407527251da..234ae37612b 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -75,7 +75,7 @@ pub fn get_attr<'a>( }) .map_or_else( || { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute"); false }, |deprecation_status| { diff --git a/tests/ui/unknown_attribute.stderr b/tests/ui/unknown_attribute.stderr index 47e37aed246..618c5980d64 100644 --- a/tests/ui/unknown_attribute.stderr +++ b/tests/ui/unknown_attribute.stderr @@ -1,4 +1,4 @@ -error: Usage of unknown attribute +error: usage of unknown attribute --> $DIR/unknown_attribute.rs:1:11 | LL | #[clippy::unknown] From fe37ddbd11dd3f2cb2e529846492b312e44ed1b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 23:45:24 +0200 Subject: [PATCH 0396/1110] suspicious-arithmetic-impl: make lint adhere to lint message convention --- clippy_lints/src/suspicious_trait_impl.rs | 4 ++-- tests/ui/suspicious_arithmetic_impl.stderr | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 502fffc5e6c..4e335a0222f 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_ARITHMETIC_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_OP_ASSIGN_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } } diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 7e42d72c30b..23d47e3f1ff 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,4 +1,4 @@ -error: Suspicious use of binary operator in `Add` impl +error: suspicious use of binary operator in `Add` impl --> $DIR/suspicious_arithmetic_impl.rs:11:20 | LL | Foo(self.0 - other.0) @@ -6,7 +6,7 @@ LL | Foo(self.0 - other.0) | = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` -error: Suspicious use of binary operator in `AddAssign` impl +error: suspicious use of binary operator in `AddAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:17:23 | LL | *self = *self - other; @@ -14,7 +14,7 @@ LL | *self = *self - other; | = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default -error: Suspicious use of binary operator in `MulAssign` impl +error: suspicious use of binary operator in `MulAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:30:16 | LL | self.0 /= other.0; From 5d69ca5e114a1e44be08c835394b82ffc9cc7f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 23:46:52 +0200 Subject: [PATCH 0397/1110] also change "deprecated-attribute" message --- clippy_lints/src/utils/attrs.rs | 2 +- tests/ui/renamed_builtin_attr.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 234ae37612b..a3975683cb3 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -80,7 +80,7 @@ pub fn get_attr<'a>( }, |deprecation_status| { let mut diag = - sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { diag.emit(); diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index a399ff52fb8..88046762483 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,4 +1,4 @@ -error: Usage of deprecated attribute +error: usage of deprecated attribute --> $DIR/renamed_builtin_attr.rs:3:11 | LL | #[clippy::cyclomatic_complexity = "1"] From 3e1e0c9bdb582b15c7804e354085b17f8b6c62d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 23 Jul 2020 23:54:34 +0200 Subject: [PATCH 0398/1110] redundant-static-lifetimes: make lint adhere to lint message convention --- .../src/redundant_static_lifetimes.rs | 4 +-- tests/ui/redundant_static_lifetimes.stderr | 32 +++++++++---------- ...redundant_static_lifetimes_multiple.stderr | 20 ++++++------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index c6f57298c26..7bbcc67aa2d 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { - self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); // Don't check associated consts because `'static` cannot be elided on those (issue // #2438) } if let ItemKind::Static(ref var_type, _, _) = item.kind { - self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "statics have by default a `'static` lifetime"); } } } diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 3c3d2eacd8d..649831f9c06 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. @@ -6,91 +6,91 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. diff --git a/tests/ui/redundant_static_lifetimes_multiple.stderr b/tests/ui/redundant_static_lifetimes_multiple.stderr index afc853dcfce..cc7e55a757a 100644 --- a/tests/ui/redundant_static_lifetimes_multiple.stderr +++ b/tests/ui/redundant_static_lifetimes_multiple.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:18 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static @@ -6,55 +6,55 @@ LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:30 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:29 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:39 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:40 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:55 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:26 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:38 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:37 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:47 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; From 81f77a411e844ca553ba93adcc8be617d372ac30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 24 Jul 2020 00:12:21 +0200 Subject: [PATCH 0399/1110] range-zip-with-len: make lint adhere to lint message convention --- clippy_lints/src/ranges.rs | 2 +- tests/ui/range.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4c1f2e8e01a..f88075798ca 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { span_lint(cx, RANGE_ZIP_WITH_LEN, expr.span, - &format!("It is more idiomatic to use `{}.iter().enumerate()`", + &format!("it is more idiomatic to use `{}.iter().enumerate()`", snippet(cx, iter_args[0].span, "_"))); } } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index d53c1edecac..dcb5061371f 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -1,4 +1,4 @@ -error: It is more idiomatic to use `v1.iter().enumerate()` +error: it is more idiomatic to use `v1.iter().enumerate()` --> $DIR/range.rs:5:14 | LL | let _x = v1.iter().zip(0..v1.len()); From 9178363574625a6185ea779c4a231b8f18205261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 24 Jul 2020 00:16:28 +0200 Subject: [PATCH 0400/1110] path-buf-push-overwrite: make lint adhere to lint message convention --- clippy_lints/src/path_buf_push_overwrite.rs | 2 +- tests/ui/path_buf_push_overwrite.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 66a145a7f14..b8583402928 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { cx, PATH_BUF_PUSH_OVERWRITE, lit.span, - "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", "try", format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), Applicability::MachineApplicable, diff --git a/tests/ui/path_buf_push_overwrite.stderr b/tests/ui/path_buf_push_overwrite.stderr index 09b18d71baf..bb8dce2bbba 100644 --- a/tests/ui/path_buf_push_overwrite.stderr +++ b/tests/ui/path_buf_push_overwrite.stderr @@ -1,4 +1,4 @@ -error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition +error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition --> $DIR/path_buf_push_overwrite.rs:7:12 | LL | x.push("/bar"); From e519bb3c850199d03eed7f2bd29637b3d5479551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 24 Jul 2020 00:18:34 +0200 Subject: [PATCH 0401/1110] overflow-check-conditional: make lint adhere to lint message convention --- clippy_lints/src/overflow_check_conditional.rs | 8 ++++---- tests/ui/overflow_check_conditional.stderr | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 4d4a9676654..3c041bac234 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -42,13 +42,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if let BinOpKind::Lt = op.node { if let BinOpKind::Add = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C overflow conditions that will fail in Rust."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } if let BinOpKind::Gt = op.node { if let BinOpKind::Sub = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C underflow conditions that will fail in Rust."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } @@ -67,13 +67,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if let BinOpKind::Gt = op.node { if let BinOpKind::Add = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C overflow conditions that will fail in Rust."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } if let BinOpKind::Lt = op.node { if let BinOpKind::Sub = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C underflow conditions that will fail in Rust."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } diff --git a/tests/ui/overflow_check_conditional.stderr b/tests/ui/overflow_check_conditional.stderr index ad66135d326..19e843c2c0a 100644 --- a/tests/ui/overflow_check_conditional.stderr +++ b/tests/ui/overflow_check_conditional.stderr @@ -1,4 +1,4 @@ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:8:8 | LL | if a + b < a {} @@ -6,43 +6,43 @@ LL | if a + b < a {} | = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:9:8 | LL | if a > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:10:8 | LL | if a + b < b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:11:8 | LL | if b > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:12:8 | LL | if a - b > b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:13:8 | LL | if b < a - b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:14:8 | LL | if a - b > a {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:15:8 | LL | if a < a - b {} From 178da9b2ef9e8c94ab0fbe7812e11445664f67b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 24 Jul 2020 00:24:11 +0200 Subject: [PATCH 0402/1110] neg-multiply: make lint adhere to lint message convention --- clippy_lints/src/neg_multiply.rs | 2 +- tests/ui/neg_multiply.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 6b6c950e0ab..aa550510867 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)); if cx.typeck_results().expr_ty(exp).is_integral(); then { - span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`"); + span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`"); } } } diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index f08bbd6a12c..ad677f6d6fb 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr @@ -1,4 +1,4 @@ -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:27:5 | LL | x * -1; @@ -6,7 +6,7 @@ LL | x * -1; | = note: `-D clippy::neg-multiply` implied by `-D warnings` -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:29:5 | LL | -1 * x; From dabf9891954aa0d0c59b230ab0e7afcfd4142be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 28 Jul 2020 12:13:22 +0200 Subject: [PATCH 0403/1110] neg-cmp-op-on-partial-ord: make lint adhere to lint message convention --- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 2 +- tests/ui/neg_cmp_op_on_partial_ord.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 95613a1b82e..0f5d5ce3495 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { cx, NEG_CMP_OP_ON_PARTIAL_ORD, expr.span, - "The use of negated comparison operators on partially ordered \ + "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor. Please \ consider using the `partial_cmp` method instead, to make it \ clear that the two values could be incomparable." diff --git a/tests/ui/neg_cmp_op_on_partial_ord.stderr b/tests/ui/neg_cmp_op_on_partial_ord.stderr index 8c5d548222e..193d9f9bcea 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.stderr +++ b/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -1,4 +1,4 @@ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21 | LL | let _not_less = !(a_value < another_value); @@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value); | = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30 | LL | let _not_less_or_equal = !(a_value <= another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 | LL | let _not_greater = !(a_value > another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33 | LL | let _not_greater_or_equal = !(a_value >= another_value); From c514ff0c93264e3cebd5eda9caf0b99fc8fd0daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 10 Aug 2020 23:50:40 +0200 Subject: [PATCH 0404/1110] Update clippy_lints/src/neg_cmp_op_on_partial_ord.rs Co-authored-by: Jane Lusby --- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 0f5d5ce3495..4fb899125e8 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -80,9 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { NEG_CMP_OP_ON_PARTIAL_ORD, expr.span, "the use of negated comparison operators on partially ordered \ - types produces code that is hard to read and refactor. Please \ + types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable." + clear that the two values could be incomparable" ) } } From 1b46e485b28613426f7bebccc009effad50fcaf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 10 Aug 2020 23:50:52 +0200 Subject: [PATCH 0405/1110] Update clippy_lints/src/unwrap.rs Co-authored-by: Jane Lusby --- clippy_lints/src/unwrap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index fd755dcc790..ea4b8172c9c 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("you checked before that `{}()` cannot fail. \ - Instead of checking and unwrapping, it's better to use `if let` or `match`", + &format!("you checked before that `{}()` cannot fail, \ + instead of checking and unwrapping, it's better to use `if let` or `match`", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, ); From f59ec1945f887eeff17ae04008fc1e6e8b1fb4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 10 Aug 2020 23:55:15 +0200 Subject: [PATCH 0406/1110] run cargo dev update-lints --- src/lintlist/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6395b571504..dc8779748e0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -412,7 +412,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "drop_bounds", group: "correctness", - desc: "Bounds of the form `T: Drop` are useless", + desc: "bounds of the form `T: Drop` are useless", deprecation: None, module: "drop_bounds", }, From 6d0b5e24dfc8232123984fcefface485aa7fbc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 00:27:55 +0200 Subject: [PATCH 0407/1110] update test stderr --- .../complex_conditionals.stderr | 20 +++++++++---------- .../complex_conditionals_nested.stderr | 2 +- .../checked_unwrap/simple_conditionals.stderr | 14 ++++++------- tests/ui/neg_cmp_op_on_partial_ord.stderr | 8 ++++---- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index 5b62dca629f..33bb5136ef8 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index 46ffc16c23e..a01f7f956f6 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index bf4b6c93098..416ec1a01ab 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { @@ -35,7 +35,7 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { @@ -44,7 +44,7 @@ LL | if x.is_none() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { @@ -57,7 +57,7 @@ LL | m!(x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { @@ -83,7 +83,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { @@ -100,7 +100,7 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { @@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { diff --git a/tests/ui/neg_cmp_op_on_partial_ord.stderr b/tests/ui/neg_cmp_op_on_partial_ord.stderr index 193d9f9bcea..c7856000721 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.stderr +++ b/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -1,4 +1,4 @@ -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21 | LL | let _not_less = !(a_value < another_value); @@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value); | = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30 | LL | let _not_less_or_equal = !(a_value <= another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 | LL | let _not_greater = !(a_value > another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33 | LL | let _not_greater_or_equal = !(a_value >= another_value); From b8713e3854cb90b974eceaa1d50484831591619c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 12:35:55 +0200 Subject: [PATCH 0408/1110] unnecessary-mut-passed: make lint message say if fn is a function or a method. --- clippy_lints/src/mut_reference.rs | 13 ++++++++++--- tests/ui/mut_reference.stderr | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index b8dc5081632..be3ae7ab380 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -39,6 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { arguments, cx.typeck_results().expr_ty(fn_expr), &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)), + "function", ); } }, @@ -46,14 +47,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); - check_arguments(cx, arguments, method_type, &path.ident.as_str()) + check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method") }, _ => (), } } } -fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) { +fn check_arguments<'tcx>( + cx: &LateContext<'tcx>, + arguments: &[Expr<'_>], + type_definition: Ty<'tcx>, + name: &str, + fn_kind: &str, +) { match type_definition.kind { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); @@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("The function/method `{}` doesn't need a mutable reference", name), + &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), ); } }, diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index fa8c82ae0f3..062d30b262c 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,4 +1,4 @@ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | LL | takes_an_immutable_reference(&mut 42); @@ -6,13 +6,13 @@ LL | takes_an_immutable_reference(&mut 42); | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` -error: The function/method `as_ptr` doesn't need a mutable reference +error: the function `as_ptr` doesn't need a mutable reference --> $DIR/mut_reference.rs:19:12 | LL | as_ptr(&mut 42); | ^^^^^^^ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the method `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:23:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); From 9311c11d7c01d64d22dc7914e9dff4c5167adb49 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 11 Aug 2020 13:57:32 +0200 Subject: [PATCH 0409/1110] Fix sync fallout --- clippy_lints/src/transmute.rs | 12 ++--- tests/compile-test.rs | 7 +-- .../transmutes_expressible_as_ptr_casts.fixed | 38 ++++++---------- .../ui/transmutes_expressible_as_ptr_casts.rs | 38 ++++++---------- ...transmutes_expressible_as_ptr_casts.stderr | 44 +++++++++---------- 5 files changed, 59 insertions(+), 80 deletions(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index f077c146183..7b5e92eb5ee 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -61,12 +61,14 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust,ignore - /// core::intrinsics::transmute::<*const [i32], *const [u16]>(p) + /// ```rust + /// # let p: *const [i32] = &[]; + /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; /// ``` /// Use instead: /// ```rust - /// p as *const [u16] + /// # let p: *const [i32] = &[]; + /// p as *const [u16]; /// ``` pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, complexity, @@ -704,14 +706,14 @@ fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::*; + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) ) } -/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 26a47d23706..e662d608edf 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -147,9 +147,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -217,6 +214,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 98288dde6d8..b6f1e83181c 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -9,60 +9,48 @@ use std::mem::{size_of, transmute}; -// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is // valid, which we quote from below. fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { - usize::MAX as *const i32 - }; + let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 }; let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { - ptr_i32 as *const i8 - }; + let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 }; let _ptr_i8 = ptr_i32 as *const i8; - let slice_ptr = &[0,1,2,3] as *const [i32]; + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast - let _ptr_to_unsized_transmute = unsafe { - slice_ptr as *const [u16] - }; + let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] }; let _ptr_to_unsized = slice_ptr as *const [u16]; // TODO: We could try testing vtable casts here too, but maybe // we should wait until std::raw::TraitObject is stabilized? // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast - let _usize_from_int_ptr_transmute = unsafe { - ptr_i32 as usize - }; + let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize }; let _usize_from_int_ptr = ptr_i32 as usize; - let array_ref: &[i32; 4] = &[1,2,3,4]; + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; // e has type &[T; n] and U is *const T; array-ptr-cast - let _array_ptr_transmute = unsafe { - array_ref as *const [i32; 4] - }; + let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] }; let _array_ptr = array_ref as *const [i32; 4]; - fn foo(_: usize) -> u8 { 42 } + fn foo(_: usize) -> u8 { + 42 + } // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast - let _usize_ptr_transmute = unsafe { - foo as *const usize - }; + let _usize_ptr_transmute = unsafe { foo as *const usize }; let _usize_ptr_transmute = foo as *const usize; // e is a function pointer type and U is an integer; fptr-addr-cast - let _usize_from_fn_ptr_transmute = unsafe { - foo as usize - }; + let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; let _usize_from_fn_ptr = foo as *const usize; } diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index fd5055c08f6..0205d1ece60 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -9,60 +9,48 @@ use std::mem::{size_of, transmute}; -// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is // valid, which we quote from below. fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { - transmute::(usize::MAX) - }; + let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { - transmute::<*const i32, *const i8>(ptr_i32) - }; + let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; let _ptr_i8 = ptr_i32 as *const i8; - let slice_ptr = &[0,1,2,3] as *const [i32]; + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast - let _ptr_to_unsized_transmute = unsafe { - transmute::<*const [i32], *const [u16]>(slice_ptr) - }; + let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; let _ptr_to_unsized = slice_ptr as *const [u16]; // TODO: We could try testing vtable casts here too, but maybe // we should wait until std::raw::TraitObject is stabilized? // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast - let _usize_from_int_ptr_transmute = unsafe { - transmute::<*const i32, usize>(ptr_i32) - }; + let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; let _usize_from_int_ptr = ptr_i32 as usize; - let array_ref: &[i32; 4] = &[1,2,3,4]; + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; // e has type &[T; n] and U is *const T; array-ptr-cast - let _array_ptr_transmute = unsafe { - transmute::<&[i32; 4], *const [i32; 4]>(array_ref) - }; + let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; let _array_ptr = array_ref as *const [i32; 4]; - fn foo(_: usize) -> u8 { 42 } + fn foo(_: usize) -> u8 { + 42 + } // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast - let _usize_ptr_transmute = unsafe { - transmute:: u8, *const usize>(foo) - }; + let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; let _usize_ptr_transmute = foo as *const usize; // e is a function pointer type and U is an integer; fptr-addr-cast - let _usize_from_fn_ptr_transmute = unsafe { - transmute:: u8, usize>(foo) - }; + let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; let _usize_from_fn_ptr = foo as *const usize; } diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 46597acc6c0..1157b179317 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,53 +1,53 @@ error: transmute from an integer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:20:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 | -LL | transmute::(usize::MAX) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` +LL | let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` | = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:26:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 | -LL | transmute::<*const i32, *const i8>(ptr_i32) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` +LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` | = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 | -LL | transmute::<*const [i32], *const [u16]>(slice_ptr) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` +LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:42:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 | -LL | transmute::<*const i32, usize>(ptr_i32) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` +LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` | = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:50:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 | -LL | transmute::<&[i32; 4], *const [i32; 4]>(array_ref) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` +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 - --> $DIR/transmutes_expressible_as_ptr_casts.rs:58:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 | -LL | transmute:: u8, *const usize>(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` +LL | let _usize_ptr_transmute = unsafe { transmute:: 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 - --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 | -LL | transmute:: u8, usize>(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` +LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:77:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` From c0a9d64818d7076b72fd6c3a9e6172eca659034b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 11:50:26 +0200 Subject: [PATCH 0410/1110] stable-sort-primitive: make lint adhere to lint message convention --- clippy_lints/src/stable_sort_primitive.rs | 6 +++--- tests/ui/stable_sort_primitive.stderr | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index cd7056620a2..22c49a20451 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -111,9 +111,9 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "Use {} instead of {}", - detection.method.unstable_name(), - detection.method.stable_name() + "used {} instead of {}", + detection.method.stable_name(), + detection.method.unstable_name() ) .as_str(), "try", diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b0b729ede48..b73012a4691 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); From ac194cafc124276d4614bf023ca7ea6e9be9c6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 11:53:21 +0200 Subject: [PATCH 0411/1110] map_clone: make lint adhere to lint message convention --- clippy_lints/src/map_clone.rs | 12 ++++++------ tests/ui/map_clone.stderr | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 641e6a17043..1cd5b201292 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { cx, MAP_CLONE, root.trim_start(receiver).unwrap(), - "You are needlessly cloning iterator elements", - "Remove the `map` call", + "you are needlessly cloning iterator elements", + "remove the `map` call", String::new(), Applicability::MachineApplicable, ) @@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for copying elements", - "Consider calling the dedicated `copied` method", + "you are using an explicit closure for copying elements", + "consider calling the dedicated `copied` method", format!( "{}.copied()", snippet_with_applicability(cx, root, "..", &mut applicability) @@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for cloning elements", - "Consider calling the dedicated `cloned` method", + "you are using an explicit closure for cloning elements", + "consider calling the dedicated `cloned` method", format!( "{}.cloned()", snippet_with_applicability(cx, root, "..", &mut applicability) diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 9eec6928e8c..4f43cff5024 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,40 +1,40 @@ -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: You are using an explicit closure for cloning elements +error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:14:26 | LL | let _: Option = Some(&16).map(|b| *b); - | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:15:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` -error: You are needlessly cloning iterator elements +error: you are needlessly cloning iterator elements --> $DIR/map_clone.rs:26:29 | LL | let _ = std::env::args().map(|v| v.clone()); - | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: aborting due to 6 previous errors From 04867e004ebc0f2edf66d0a457e785848451f13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 12:10:42 +0200 Subject: [PATCH 0412/1110] mutex-atomic: make lint adhere to lint message convention --- clippy_lints/src/mutex_atomic.rs | 4 ++-- tests/ui/mutex_atomic.stderr | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 568898aa5c9..21efee71269 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`.", + "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`", atomic_name ); match mutex_param.kind { diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 7dac0865855..a3511ba708a 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,4 +1,4 @@ -error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:6:5 | LL | Mutex::new(true); @@ -6,31 +6,31 @@ LL | Mutex::new(true); | = note: `-D clippy::mutex-atomic` implied by `-D warnings` -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:7:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:11:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(0u32); @@ -38,7 +38,7 @@ LL | Mutex::new(0u32); | = note: `-D clippy::mutex-integer` implied by `-D warnings` -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(0i32); From 6af297f80e59050c87078f1ba6f05c97d6f90fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 12:42:50 +0200 Subject: [PATCH 0413/1110] iter-next-slice: make lint adhere to lint message convention --- clippy_lints/src/methods/mod.rs | 4 ++-- tests/ui/iter_next_slice.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 570ae66d595..f4eb9c4516f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2280,7 +2280,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on a Slice without end index.", + "using `.iter().next()` on a Slice without end index", "try calling", format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), applicability, @@ -2299,7 +2299,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on an array", + "using `.iter().next()` on an array", "try calling", format!( "{}.get(0)", diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index bbf61df0cda..8c10a252ee0 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -1,4 +1,4 @@ -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:5 | LL | s.iter().next(); @@ -6,19 +6,19 @@ LL | s.iter().next(); | = note: `-D clippy::iter-next-slice` implied by `-D warnings` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:12:5 | LL | s[2..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:15:5 | LL | v[5..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:5 | LL | v.iter().next(); From f171f89aed11043e459c3baab305e7f859debb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 15:14:32 +0200 Subject: [PATCH 0414/1110] int_plus_one: make lint adhere to lint message convention --- clippy_lints/src/int_plus_one.rs | 6 +++--- tests/ui/int_plus_one.stderr | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index e91fb0c2f27..c629ee05ab9 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -152,7 +152,7 @@ impl IntPlusOne { cx, INT_PLUS_ONE, block.span, - "Unnecessary `>= y + 1` or `x - 1 >=`", + "unnecessary `>= y + 1` or `x - 1 >=`", "change it to", recommendation, Applicability::MachineApplicable, // snippet @@ -163,8 +163,8 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec.clone()); + if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { + Self::emit_warning(cx, item, rec); } } } diff --git a/tests/ui/int_plus_one.stderr b/tests/ui/int_plus_one.stderr index 29a6914761c..c5b020ba8ce 100644 --- a/tests/ui/int_plus_one.stderr +++ b/tests/ui/int_plus_one.stderr @@ -1,4 +1,4 @@ -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:9:13 | LL | let _ = x >= y + 1; @@ -6,19 +6,19 @@ LL | let _ = x >= y + 1; | = note: `-D clippy::int-plus-one` implied by `-D warnings` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:10:13 | LL | let _ = y + 1 <= x; | ^^^^^^^^^^ help: change it to: `y < x` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:12:13 | LL | let _ = x - 1 >= y; | ^^^^^^^^^^ help: change it to: `x > y` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:13:13 | LL | let _ = y <= x - 1; From bdf4dc3abd9a49f699d9de209a1f4d55ce770191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 15:22:59 +0200 Subject: [PATCH 0415/1110] implicit-saturating-sub: make lint adhere to lint message convention --- clippy_lints/src/implicit_saturating_sub.rs | 4 +- tests/ui/implicit_saturating_sub.stderr | 46 ++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5f931a0adde..b57fe8dc426 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { cx, IMPLICIT_SATURATING_SUB, expr.span, - "Implicitly performing saturating subtraction", + "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()), + format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), Applicability::MachineApplicable, ); } diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 2eb2023b3b9..5bb9a606422 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,4 +1,4 @@ -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:13:5 | LL | / if u_8 > 0 { @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:20:13 | LL | / if u_8 > 0 { @@ -16,7 +16,7 @@ LL | | u_8 -= 1; LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:34:5 | LL | / if u_16 > 0 { @@ -24,7 +24,7 @@ LL | | u_16 -= 1; LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:44:5 | LL | / if u_32 != 0 { @@ -32,7 +32,7 @@ LL | | u_32 -= 1; LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:65:5 | LL | / if u_64 > 0 { @@ -40,7 +40,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:70:5 | LL | / if 0 < u_64 { @@ -48,7 +48,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:75:5 | LL | / if 0 != u_64 { @@ -56,7 +56,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:96:5 | LL | / if u_usize > 0 { @@ -64,7 +64,7 @@ LL | | u_usize -= 1; LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:108:5 | LL | / if i_8 > i8::MIN { @@ -72,7 +72,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:113:5 | LL | / if i_8 > i8::MIN { @@ -80,7 +80,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:118:5 | LL | / if i_8 != i8::MIN { @@ -88,7 +88,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 != i8::MIN { @@ -96,7 +96,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_16 > i16::MIN { @@ -104,7 +104,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_16 > i16::MIN { @@ -112,7 +112,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:143:5 | LL | / if i_16 != i16::MIN { @@ -120,7 +120,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 != i16::MIN { @@ -128,7 +128,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_32 > i32::MIN { @@ -136,7 +136,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_32 > i32::MIN { @@ -144,7 +144,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:168:5 | LL | / if i_32 != i32::MIN { @@ -152,7 +152,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 != i32::MIN { @@ -160,7 +160,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i64::MIN < i_64 { @@ -168,7 +168,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i64::MIN != i_64 { @@ -176,7 +176,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:193:5 | LL | / if i64::MIN < i_64 { From 1f17c3b02bce95c7c95a320e9e6e8e88b216d235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 15:28:51 +0200 Subject: [PATCH 0416/1110] multiple_inherent_impl: make lint adhere to lint message convention --- clippy_lints/src/inherent_impl.rs | 4 ++-- tests/ui/impl.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 9fb10c7f627..4e6bb604785 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { cx, MULTIPLE_INHERENT_IMPL, *additional_span, - "Multiple implementations of this structure", + "multiple implementations of this structure", |diag| { - diag.span_note(*initial_span, "First implementation here"); + diag.span_note(*initial_span, "first implementation here"); }, ) }) diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index 585d32845d2..aab688cc2d8 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -1,4 +1,4 @@ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:10:1 | LL | / impl MyStruct { @@ -7,7 +7,7 @@ LL | | } | |_^ | = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { @@ -15,7 +15,7 @@ LL | | fn first() {} LL | | } | |_^ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:24:5 | LL | / impl super::MyStruct { @@ -23,7 +23,7 @@ LL | | fn third() {} LL | | } | |_____^ | -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { From 423615693ba27f77f2e01a82948bbe592f48f6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 16:28:05 +0200 Subject: [PATCH 0417/1110] pub-enum-variant-names: make lint adhere to lint message convention --- clippy_lints/src/enum_variants.rs | 2 +- tests/ui/enum_variants.stderr | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index cb0fd59a2d4..d73d0f1752e 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -227,7 +227,7 @@ fn check_variant( cx, lint, span, - &format!("All variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {}fix: `{}`", what, value), None, &format!( "remove the {}fixes and use full paths to \ diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 2835391de7f..3aa0e4ddcfe 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -24,7 +24,7 @@ error: Variant name starts with the enum's name LL | FoodBad, | ^^^^^^^ -error: All variants have the same prefix: `Food` +error: all variants have the same prefix: `Food` --> $DIR/enum_variants.rs:26:1 | LL | / enum Food { @@ -36,7 +36,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `CallType` +error: all variants have the same prefix: `CallType` --> $DIR/enum_variants.rs:36:1 | LL | / enum BadCallType { @@ -48,7 +48,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Constant` +error: all variants have the same prefix: `Constant` --> $DIR/enum_variants.rs:48:1 | LL | / enum Consts { @@ -60,7 +60,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:82:1 | LL | / enum Seallll { @@ -72,7 +72,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Prefix` +error: all variants have the same prefix: `Prefix` --> $DIR/enum_variants.rs:88:1 | LL | / enum NonCaps { @@ -84,7 +84,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:94:1 | LL | / pub enum PubSeall { From 2de290d5c5e1d2f0c0f112a51f0cba2e0cb91636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 16:31:02 +0200 Subject: [PATCH 0418/1110] duration-subsec: make lint adhere to lint message convention --- clippy_lints/src/duration_subsec.rs | 2 +- tests/ui/duration_subsec.stderr | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 1dfb2eaa579..8ece44878fe 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { cx, DURATION_SUBSEC, expr.span, - &format!("Calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{}()` is more concise than this calculation", suggested_fn), "try", format!( "{}.{}()", diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index bd8adc2c570..cdbeff6a037 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -1,4 +1,4 @@ -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:10:24 | LL | let bad_millis_1 = dur.subsec_micros() / 1_000; @@ -6,25 +6,25 @@ LL | let bad_millis_1 = dur.subsec_micros() / 1_000; | = note: `-D clippy::duration-subsec` implied by `-D warnings` -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:11:24 | LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:16:22 | LL | let bad_micros = dur.subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:21:13 | LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:25:13 | LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; From db390f5e6a2e68a0f9e0d235f2b734e907cafef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 16:35:09 +0200 Subject: [PATCH 0419/1110] enum-clike-unportable-variant: tweak message a bit (Clike -> C-like) --- clippy_lints/src/enum_clike.rs | 2 +- tests/ui/enum_clike_unportable_variant.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 91214f277be..48caf48dbdb 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { cx, ENUM_CLIKE_UNPORTABLE_VARIANT, var.span, - "Clike enum variant discriminant is not portable to 32-bit targets", + "C-like enum variant discriminant is not portable to 32-bit targets", ); }; } diff --git a/tests/ui/enum_clike_unportable_variant.stderr b/tests/ui/enum_clike_unportable_variant.stderr index 71f3f5e083e..5935eea5e03 100644 --- a/tests/ui/enum_clike_unportable_variant.stderr +++ b/tests/ui/enum_clike_unportable_variant.stderr @@ -1,4 +1,4 @@ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:8:5 | LL | X = 0x1_0000_0000, @@ -6,49 +6,49 @@ LL | X = 0x1_0000_0000, | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:15:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:18:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:25:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:26:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:28:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:35:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:40:5 | LL | X = ::Number, From 89591a78b83df30830bcc8f6fe57f6fe1fbf918e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 16:38:20 +0200 Subject: [PATCH 0420/1110] enum-variant-names: make lint adhere to lint message convention --- clippy_lints/src/enum_variants.rs | 4 ++-- tests/ui/enum_variants.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d73d0f1752e..a9294a87f15 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -183,10 +183,10 @@ fn check_variant( && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) { - span_lint(cx, lint, var.span, "Variant name starts with the enum's name"); + span_lint(cx, lint, var.span, "variant name starts with the enum's name"); } if partial_rmatch(item_name, &name) == item_name_chars { - span_lint(cx, lint, var.span, "Variant name ends with the enum's name"); + span_lint(cx, lint, var.span, "variant name ends with the enum's name"); } } let first = &def.variants[0].ident.name.as_str(); diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 3aa0e4ddcfe..b1d481190ff 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -1,4 +1,4 @@ -error: Variant name ends with the enum's name +error: variant name ends with the enum's name --> $DIR/enum_variants.rs:16:5 | LL | cFoo, @@ -6,19 +6,19 @@ LL | cFoo, | = note: `-D clippy::enum-variant-names` implied by `-D warnings` -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:27:5 | LL | FoodGood, | ^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:28:5 | LL | FoodMiddle, | ^^^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:29:5 | LL | FoodBad, From 605e027fda4420669784864940abcbabef5b0efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 16:40:45 +0200 Subject: [PATCH 0421/1110] if_let_some_result: make lint adhere to lint message convention --- clippy_lints/src/if_let_some_result.rs | 4 ++-- tests/ui/if_let_some_result.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 5b22df5fe49..28b20cdeac3 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { cx, IF_LET_SOME_RESULT, expr.span.with_hi(op.span.hi()), - "Matching on `Some` with `ok()` is redundant", - &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), sugg, applicability, ); diff --git a/tests/ui/if_let_some_result.stderr b/tests/ui/if_let_some_result.stderr index 334ccb01016..6afee0f36b9 100644 --- a/tests/ui/if_let_some_result.stderr +++ b/tests/ui/if_let_some_result.stderr @@ -1,22 +1,22 @@ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | LL | if let Some(y) = x.parse().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x.parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:24:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x . parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From be3e695b60b07e911a88f6cb660c5617836c5365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 11 Aug 2020 16:43:53 +0200 Subject: [PATCH 0422/1110] if_not_else: make lint adhere to lint message convention --- clippy_lints/src/if_not_else.rs | 4 ++-- clippy_lints/src/use_self.rs | 2 +- src/lintlist/mod.rs | 2 +- tests/ui/if_not_else.stderr | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index c11e291f98e..b86d2e76656 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,7 +60,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary boolean `not` operation", + "unnecessary boolean `not` operation", None, "remove the `!` and swap the blocks of the `if`/`else`", ); @@ -70,7 +70,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary `!=` operation", + "unnecessary `!=` operation", None, "change to `==` and swap the blocks of the `if`/`else`", ); diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 776c6bc57ca..427a1b65773 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ``` pub USE_SELF, nursery, - "Unnecessary structure name repetition whereas `Self` is applicable" + "unnecessary structure name repetition whereas `Self` is applicable" } declare_lint_pass!(UseSelf => [USE_SELF]); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bbb300296be..ccc9e250952 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2498,7 +2498,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "use_self", group: "nursery", - desc: "Unnecessary structure name repetition whereas `Self` is applicable", + desc: "unnecessary structure name repetition whereas `Self` is applicable", deprecation: None, module: "use_self", }, diff --git a/tests/ui/if_not_else.stderr b/tests/ui/if_not_else.stderr index 78bc4d4bd20..53d1b86d02a 100644 --- a/tests/ui/if_not_else.stderr +++ b/tests/ui/if_not_else.stderr @@ -1,4 +1,4 @@ -error: Unnecessary boolean `not` operation +error: unnecessary boolean `not` operation --> $DIR/if_not_else.rs:9:5 | LL | / if !bla() { @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::if-not-else` implied by `-D warnings` = help: remove the `!` and swap the blocks of the `if`/`else` -error: Unnecessary `!=` operation +error: unnecessary `!=` operation --> $DIR/if_not_else.rs:14:5 | LL | / if 4 != 5 { From 8a96b9cdfe408106fff94745fee1223b2e3ddb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 12 Aug 2020 14:27:06 +0200 Subject: [PATCH 0423/1110] write.rs: don't clone TokenStream --- clippy_lints/src/write.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 063f94582b9..5f88dcb188a 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -237,7 +237,7 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( cx, @@ -252,7 +252,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(print) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -273,7 +273,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -294,7 +294,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(writeln) { - if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; let suggestion = expr.map_or_else( @@ -364,17 +364,11 @@ impl Write { /// (Some("string to write: {}"), Some(buf)) /// ``` #[allow(clippy::too_many_lines)] - fn check_tts<'a>( - &self, - cx: &EarlyContext<'a>, - tts: &TokenStream, - is_write: bool, - ) -> (Option, Option) { + fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { use rustc_parse_format::{ AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser, Piece, }; - let tts = tts.clone(); let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); let mut expr: Option = None; From 7d2e42daec1a56ad8f70a2b146bd842e98e0430d Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Wed, 12 Aug 2020 08:54:32 -0600 Subject: [PATCH 0424/1110] fix typo pointed out in review comment Co-authored-by: Philipp Krones --- clippy_lints/src/ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7f58a381adc..31da45f6b8a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// this lint on them will fix the problem, but they may be in other crates. /// /// One notable example of a function that may cause issues, and which cannot - /// easily be changed due to beinng in the standard library is `Vec::contains`. + /// easily be changed due to being in the standard library is `Vec::contains`. /// when called on a `Vec>`. If a `&Vec` is passed to that method then /// it will compile, but if a `&[T]` is passed then it will not compile. /// From 0fc61becb5f1b9a183311c8b053d996b8695a071 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 07:30:55 -0600 Subject: [PATCH 0425/1110] Add the other overloadable operations to suspicious_arithmetic_impl In #2268 I idly mused that the other user-overloadable operations could be added to this lint. Knowing that the lint was arguably incomplete was gnawing at the back of my mind, so I figured that I might as well make this PR, particularly given the change needed was so small. --- clippy_lints/src/suspicious_trait_impl.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 4e335a0222f..e026f27d9a7 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -86,12 +86,18 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, expr, binop.node, - &["Add", "Sub", "Mul", "Div"], + &["Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr"], &[ hir::BinOpKind::Add, hir::BinOpKind::Sub, hir::BinOpKind::Mul, hir::BinOpKind::Div, + hir::BinOpKind::Rem, + hir::BinOpKind::BitAnd, + hir::BinOpKind::BitOr, + hir::BinOpKind::BitXor, + hir::BinOpKind::Shl, + hir::BinOpKind::Shr, ], ) { span_lint( From 616682deb72aa3760b1af3c701090e894f227ac7 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 09:18:16 -0600 Subject: [PATCH 0426/1110] formatting --- clippy_lints/src/suspicious_trait_impl.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index e026f27d9a7..596d4eb6141 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -86,7 +86,9 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, expr, binop.node, - &["Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr"], + &[ + "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr" + ], &[ hir::BinOpKind::Add, hir::BinOpKind::Sub, From c70581732de89a1b4f064818edeca9a18913ded2 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 09:21:20 -0600 Subject: [PATCH 0427/1110] trailing comma Should have actually ran rustfmt on it, rather than attempting to fix it manually --- clippy_lints/src/suspicious_trait_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 596d4eb6141..3a688a7bbef 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { expr, binop.node, &[ - "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr" + "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr", ], &[ hir::BinOpKind::Add, From f4eeff99b6f2d5a01f7af1eae46e1b84525bf95a Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Wed, 12 Aug 2020 09:17:40 -0600 Subject: [PATCH 0428/1110] add tests for Rem, BitAnd, BitOr, BitXor, Shl, and Shr --- tests/ui/suspicious_arithmetic_impl.rs | 52 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs index 60c2f3ec9b6..5c280efac1a 100644 --- a/tests/ui/suspicious_arithmetic_impl.rs +++ b/tests/ui/suspicious_arithmetic_impl.rs @@ -1,5 +1,7 @@ #![warn(clippy::suspicious_arithmetic_impl)] -use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub}; +use std::ops::{ + Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub, +}; #[derive(Copy, Clone)] struct Foo(u32); @@ -61,6 +63,54 @@ impl Div for Foo { } } +impl Rem for Foo { + type Output = Foo; + + fn rem(self, other: Self) -> Self { + Foo(self.0 / other.0) + } +} + +impl BitAnd for Foo { + type Output = Foo; + + fn bitand(self, other: Self) -> Self { + Foo(self.0 | other.0) + } +} + +impl BitOr for Foo { + type Output = Foo; + + fn bitor(self, other: Self) -> Self { + Foo(self.0 ^ other.0) + } +} + +impl BitXor for Foo { + type Output = Foo; + + fn bitxor(self, other: Self) -> Self { + Foo(self.0 & other.0) + } +} + +impl Shl for Foo { + type Output = Foo; + + fn shl(self, other: Self) -> Self { + Foo(self.0 >> other.0) + } +} + +impl Shr for Foo { + type Output = Foo; + + fn shr(self, other: Self) -> Self { + Foo(self.0 << other.0) + } +} + struct Bar(i32); impl Add for Bar { From 7bd7a46331fbaa8b8ebbaacf178c988498df9f13 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Wed, 12 Aug 2020 10:49:12 -0600 Subject: [PATCH 0429/1110] run tests/ui/update-references.sh to update 'suspicious_arithmetic_impl.rs' --- tests/ui/suspicious_arithmetic_impl.stderr | 44 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 23d47e3f1ff..388fc740082 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,5 +1,5 @@ error: suspicious use of binary operator in `Add` impl - --> $DIR/suspicious_arithmetic_impl.rs:11:20 + --> $DIR/suspicious_arithmetic_impl.rs:13:20 | LL | Foo(self.0 - other.0) | ^ @@ -7,7 +7,7 @@ LL | Foo(self.0 - other.0) = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` error: suspicious use of binary operator in `AddAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:17:23 + --> $DIR/suspicious_arithmetic_impl.rs:19:23 | LL | *self = *self - other; | ^ @@ -15,10 +15,46 @@ LL | *self = *self - other; = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default error: suspicious use of binary operator in `MulAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:30:16 + --> $DIR/suspicious_arithmetic_impl.rs:32:16 | LL | self.0 /= other.0; | ^^ -error: aborting due to 3 previous errors +error: suspicious use of binary operator in `Rem` impl + --> $DIR/suspicious_arithmetic_impl.rs:70:20 + | +LL | Foo(self.0 / other.0) + | ^ + +error: suspicious use of binary operator in `BitAnd` impl + --> $DIR/suspicious_arithmetic_impl.rs:78:20 + | +LL | Foo(self.0 | other.0) + | ^ + +error: suspicious use of binary operator in `BitOr` impl + --> $DIR/suspicious_arithmetic_impl.rs:86:20 + | +LL | Foo(self.0 ^ other.0) + | ^ + +error: suspicious use of binary operator in `BitXor` impl + --> $DIR/suspicious_arithmetic_impl.rs:94:20 + | +LL | Foo(self.0 & other.0) + | ^ + +error: suspicious use of binary operator in `Shl` impl + --> $DIR/suspicious_arithmetic_impl.rs:102:20 + | +LL | Foo(self.0 >> other.0) + | ^^ + +error: suspicious use of binary operator in `Shr` impl + --> $DIR/suspicious_arithmetic_impl.rs:110:20 + | +LL | Foo(self.0 << other.0) + | ^^ + +error: aborting due to 9 previous errors From 480ccc3dbec4440bea0aa1f47d2ad21ebcdd578e Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 11 Aug 2020 18:01:10 -0700 Subject: [PATCH 0430/1110] Change Rc> recommendation to be Rc instead of Box --- clippy_lints/src/types.rs | 15 +++++++++++++-- tests/ui/redundant_allocation.fixed | 2 +- tests/ui/redundant_allocation.stderr | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c3dea447521..78cebc30472 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -353,14 +353,25 @@ impl Types { ); return; // don't recurse into the type } - if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) { + if match_type_parameter(cx, qpath, &paths::BOX).is_some() { + let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => panic!("Box that isn't a type"), + }, + _ => panic!("Rc without type argument"), + }; + let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => panic!("Box without type argument"), + }; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), + format!("Rc<{}>", snippet(cx, inner_span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/tests/ui/redundant_allocation.fixed b/tests/ui/redundant_allocation.fixed index 26635833458..6514fd6d1ac 100644 --- a/tests/ui/redundant_allocation.fixed +++ b/tests/ui/redundant_allocation.fixed @@ -33,7 +33,7 @@ pub fn test5(a: Rc) {} // Rc> -pub fn test6(a: Box) {} +pub fn test6(a: Rc) {} // Box<&T> diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index eaa57ce3024..92e4f67f5db 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -28,7 +28,7 @@ error: usage of `Rc>` --> $DIR/redundant_allocation.rs:36:17 | LL | pub fn test6(a: Rc>) {} - | ^^^^^^^^^^^^^ help: try: `Box` + | ^^^^^^^^^^^^^ help: try: `Rc` error: usage of `Box<&T>` --> $DIR/redundant_allocation.rs:40:22 From 5634c8da02862653be557c6ab1242a6c9ce86ce8 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 12 Aug 2020 21:37:27 +0200 Subject: [PATCH 0431/1110] Fix: keep parenthesis for suggestion in `useless_conversion` lint --- clippy_lints/src/useless_conversion.rs | 5 +++-- tests/ui/useless_conversion.fixed | 5 +++++ tests/ui/useless_conversion.rs | 5 +++++ tests/ui/useless_conversion.stderr | 8 +++++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1bf37632e32..4ab2b5e796d 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,3 +1,4 @@ +use crate::utils::sugg::Sugg; use crate::utils::{ get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, @@ -158,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if TyS::same_type(a, b); then { - let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( @@ -167,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { e.span, "useless conversion to the same type", &sugg_msg, - sugg, + sugg.to_string(), Applicability::MachineApplicable, // snippet ); } diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 813cdaecaa9..8a9b0cd3cf0 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -64,4 +64,9 @@ fn main() { let _ = "".lines(); let _ = vec![1, 2, 3].into_iter(); let _: String = format!("Hello {}", "world"); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = (a + b) * 3; } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 540fea23b36..4faa1572973 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -64,4 +64,9 @@ fn main() { let _ = "".lines().into_iter(); let _ = vec![1, 2, 3].into_iter().into_iter(); let _: String = format!("Hello {}", "world").into(); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = i32::from(a + b) * 3; } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index b958b035452..f1e880d2696 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -64,5 +64,11 @@ error: useless conversion to the same type LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: aborting due to 10 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:71:13 + | +LL | let _ = i32::from(a + b) * 3; + | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` + +error: aborting due to 11 previous errors From 4e28d99f413572087a74e1a70b17f051a08d3821 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 12 Aug 2020 13:24:55 -0700 Subject: [PATCH 0432/1110] Replace panics with early returns --- clippy_lints/src/types.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 78cebc30472..e0204273197 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -357,13 +357,13 @@ impl Types { let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { GenericArg::Type(ty) => match &ty.kind { TyKind::Path(qpath) => qpath, - _ => panic!("Box that isn't a type"), + _ => return, }, - _ => panic!("Rc without type argument"), + _ => return, }; let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { GenericArg::Type(ty) => ty.span, - _ => panic!("Box without type argument"), + _ => return, }; span_lint_and_sugg( cx, From 9f827abeb0ad8f4e3ed37af462907a31be7f7cdb Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 13 Aug 2020 09:02:49 +0900 Subject: [PATCH 0433/1110] Add reference to rustc-dev-guide about lint message --- doc/adding_lints.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 168092f7329..3c782e9b17f 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -295,8 +295,14 @@ impl EarlyLintPass for FooFunctions { Running our UI test should now produce output that contains the lint message. +According to [the rustc-dev-guide], the text should be matter of fact and avoid +capitalization and periods, unless multiple sentences are needed. +When code or an identifier must appear in a message or label, it should be +surrounded with single acute accents \`. + [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html ## Adding the lint logic From 48a142559de48ff38326e8276ffdfce9ef3b5c95 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Thu, 13 Aug 2020 13:52:21 -0600 Subject: [PATCH 0434/1110] docs: typo in `temporary_cstring_as_ptr`: s/point/&s --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f4eb9c4516f..b6266ef2ba1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -799,7 +799,7 @@ declare_clippy_lint! { /// call_some_ffi_func(c_str); /// } /// ``` - /// Here `c_str` point to a freed address. The correct use would be: + /// Here `c_str` points to a freed address. The correct use would be: /// ```rust /// # use std::ffi::CString; /// # fn call_some_ffi_func(_: *const i8) {} From 8514b8407ac83dc02532c82c9188c49967d9a5d6 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 14 Aug 2020 14:13:35 +0200 Subject: [PATCH 0435/1110] appreciative too_large_for_stack in useless `vec!` Fixes: #5847 --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/conf.rs | 2 +- clippy_lints/src/vec.rs | 97 +++++++++++++++++++++------------- tests/ui/vec.fixed | 7 +++ tests/ui/vec.rs | 7 +++ 5 files changed, 76 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..4a4445200a1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -930,11 +930,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); let too_large_for_stack = conf.too_large_for_stack; store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); + store.register_late_pass(move || box vec::UselessVec{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); - store.register_late_pass(|| box vec::UselessVec); store.register_late_pass(|| box drop_bounds::DropBounds); store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ba3492a6fff..292dbd7ad6b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -138,7 +138,7 @@ define_Conf! { (type_complexity_threshold, "type_complexity_threshold": u64, 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), - /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (too_large_for_stack, "too_large_for_stack": u64, 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index f2e76442a19..84e907d7125 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,13 +1,20 @@ -use crate::consts::constant; +use crate::consts::{constant, Constant}; +use crate::rustc_target::abi::LayoutOf; use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct UselessVec { + pub too_large_for_stack: u64, +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would /// be possible. @@ -31,7 +38,7 @@ declare_clippy_lint! { "useless `vec!`" } -declare_lint_pass!(UselessVec => [USELESS_VEC]); +impl_lint_pass!(UselessVec => [USELESS_VEC]); impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -42,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, expr.span); } } @@ -60,46 +67,62 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, span); } } } } -fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { - let mut applicability = Applicability::MachineApplicable; - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if constant(cx, cx.typeck_results(), len).is_some() { - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - if let Some(last) = args.iter().last() { - let span = args[0].span.to(last.span); +impl UselessVec { + fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + let mut applicability = Applicability::MachineApplicable; + let snippet = match *vec_args { + higher::VecArgs::Repeat(elem, len) => { + if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + #[allow(clippy::cast_possible_truncation)] + if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { + return; + } - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) - } else { - "&[]".into() - } - }, - }; + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Some(last) = args.iter().last() { + #[allow(clippy::cast_possible_truncation)] + if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { + return; + } + let span = args[0].span.to(last.span); - span_lint_and_sugg( - cx, - USELESS_VEC, - span, - "useless use of `vec!`", - "you can use a slice directly", - snippet, - applicability, - ); + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + } else { + "&[]".into() + } + }, + }; + + span_lint_and_sugg( + cx, + USELESS_VEC, + span, + "useless use of `vec!`", + "you can use a slice directly", + snippet, + applicability, + ); + } +} + +fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { + let ty = cx.typeck_results().expr_ty_adjusted(expr); + cx.layout_of(ty).map_or(0, |l| l.size.bytes()) } /// Returns the item type of the vector (i.e., the `T` in `Vec`). diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index e73a791891f..85677159620 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 3eb960f53d7..03b8ee81665 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } From 8e549978e58fe724c4637ab71808ff8785743c61 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 22 Jul 2020 21:44:53 +0900 Subject: [PATCH 0436/1110] Don't use `to_string` in impl Display --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/to_string_in_display.rs | 100 +++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/to_string_in_display.rs | 55 +++++++++++++ tests/ui/to_string_in_display.stderr | 10 +++ 6 files changed, 178 insertions(+) create mode 100644 clippy_lints/src/to_string_in_display.rs create mode 100644 tests/ui/to_string_in_display.rs create mode 100644 tests/ui/to_string_in_display.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..71433773254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1723,6 +1723,7 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..7ae185103a3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -296,6 +296,7 @@ mod swap; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; +mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; @@ -788,6 +789,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &to_string_in_display::TO_STRING_IN_DISPLAY, &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, @@ -1017,6 +1019,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box reference::DerefAddrOf); store.register_early_pass(|| box reference::RefInDeref); store.register_early_pass(|| box double_parens::DoubleParens); + store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new()); store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); store.register_early_pass(|| box if_not_else::IfNotElse); store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); @@ -1427,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), @@ -1708,6 +1712,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs new file mode 100644 index 00000000000..11bdd27d9b1 --- /dev/null +++ b/clippy_lints/src/to_string_in_display.rs @@ -0,0 +1,100 @@ +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for uses of `to_string()` in `Display` traits. + /// + /// **Why is this bad?** Usually `to_string` is implemented indirectly + /// via `Display`. Hence using it while implementing `Display` would + /// lead to infinite recursion. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.to_string()) + /// } + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// ``` + pub TO_STRING_IN_DISPLAY, + correctness, + "to_string method used while implementing Display trait" +} + +#[derive(Default)] +pub struct ToStringInDisplay { + in_display_impl: bool, +} + +impl ToStringInDisplay { + pub fn new() -> Self { + Self { in_display_impl: false } + } +} + +impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]); + +impl LateLintPass<'_> for ToStringInDisplay { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = true; + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = false; + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind; + if path.ident.name == sym!(to_string); + if match_trait_method(cx, expr, &paths::TO_STRING); + if self.in_display_impl; + + then { + span_lint( + cx, + TO_STRING_IN_DISPLAY, + expr.span, + "Using to_string in fmt::Display implementation might lead to infinite recursion", + ); + } + } + } +} + +fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if_chain! { + if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind; + if let Some(did) = trait_ref.trait_def_id(); + then { + match_def_path(cx, did, &paths::DISPLAY_TRAIT) + } else { + false + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..46827084a60 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "to_digit_is_some", }, + Lint { + name: "to_string_in_display", + group: "correctness", + desc: "to_string method used while implementing Display trait", + deprecation: None, + module: "to_string_in_display", + }, Lint { name: "todo", group: "restriction", diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs new file mode 100644 index 00000000000..3b46324704e --- /dev/null +++ b/tests/ui/to_string_in_display.rs @@ -0,0 +1,55 @@ +#![warn(clippy::to_string_in_display)] +#![allow(clippy::inherent_to_string_shadow_display)] + +use std::fmt; + +struct A; +impl A { + fn fmt(&self) { + self.to_string(); + } +} + +trait B { + fn fmt(&self) {} +} + +impl B for A { + fn fmt(&self) { + self.to_string(); + } +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn fmt(a: A) { + a.to_string(); +} + +struct C; + +impl C { + fn to_string(&self) -> String { + String::from("I am C") + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn main() { + let a = A; + a.to_string(); + a.fmt(); + fmt(a); + + let c = C; + c.to_string(); +} diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr new file mode 100644 index 00000000000..cbc0a41036b --- /dev/null +++ b/tests/ui/to_string_in_display.stderr @@ -0,0 +1,10 @@ +error: Using to_string in fmt::Display implementation might lead to infinite recursion + --> $DIR/to_string_in_display.rs:25:25 + | +LL | write!(f, "{}", self.to_string()) + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::to-string-in-display` implied by `-D warnings` + +error: aborting due to previous error + From f98ffa271d0112d04b482e1d61228d99bf006ccf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 14 Aug 2020 22:54:12 +0900 Subject: [PATCH 0437/1110] Fix FP for `same_item_push` Don't emit a lint when `pushed_item` was declared as mutable variable. --- clippy_lints/src/loops.rs | 35 ++++++++++++++++++++++++++++++----- tests/ui/same_item_push.rs | 8 ++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8352a8a3d2c..f7db2563d2b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1141,11 +1141,36 @@ fn detect_same_item_push<'tcx>( if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values - if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id) { + let node = cx.tcx.hir().get(hir_id); + if_chain! { + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + then { + match bind_ann { + BindingAnnotation::RefMut | BindingAnnotation::Mutable => {}, + _ => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } + } + } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index ff1088f86f6..bfe27e02044 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -86,4 +86,12 @@ fn main() { for a in vec_a { vec12.push(2u8.pow(a.kind)); } + + // Fix #5902 + let mut vec13: Vec = Vec::new(); + let mut item = 0; + for _ in 0..10 { + vec13.push(item); + item += 10; + } } From 72d2c2eab42fe8c7247e4a45b01a6e7411898443 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Sun, 9 Aug 2020 17:47:11 +0200 Subject: [PATCH 0438/1110] Lint `push_str` with a single-character string literal Fixes #5875 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/single_char_push_str.rs | 62 ++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 +++ tests/ui/single_char_push_str.fixed | 10 ++++ tests/ui/single_char_push_str.rs | 10 ++++ tests/ui/single_char_push_str.stderr | 16 ++++++ 8 files changed, 112 insertions(+) create mode 100644 clippy_lints/src/single_char_push_str.rs create mode 100644 tests/ui/single_char_push_str.fixed create mode 100644 tests/ui/single_char_push_str.rs create mode 100644 tests/ui/single_char_push_str.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..3b9cbbf0dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1699,6 +1699,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..4f261ba932e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,6 +287,7 @@ mod repeat_once; mod returns; mod serde_api; mod shadow; +mod single_char_push_str; mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; @@ -775,6 +776,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, + &single_char_push_str::SINGLE_CHAR_PUSH_STR, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, @@ -932,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); + store.register_late_pass(|| box single_char_push_str::SingleCharPushStrPass); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box vec::UselessVec); @@ -1416,6 +1419,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), @@ -1556,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/single_char_push_str.rs b/clippy_lints/src/single_char_push_str.rs new file mode 100644 index 00000000000..68bbef7261a --- /dev/null +++ b/clippy_lints/src/single_char_push_str.rs @@ -0,0 +1,62 @@ +use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** This is in all probability not the intended outcome. At + /// the least it hurts readability of the code. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + +declare_lint_pass!(SingleCharPushStrPass => [SINGLE_CHAR_PUSH_STR]); + +impl<'tcx> LateLintPass<'tcx> for SingleCharPushStrPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; + if let [base_string, extension_string] = args; + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, fn_def_id, &paths::PUSH_STR); + if let ExprKind::Lit(ref lit) = extension_string.kind; + if let LitKind::Str(symbol,_) = lit.node; + let extension_string_val = symbol.as_str().to_string(); + if extension_string_val.len() == 1; + then { + let mut applicability = Applicability::MachineApplicable; + let base_string_snippet = snippet_with_applicability(cx, base_string.span, "_", &mut applicability); + let sugg = format!("{}.push({:?})", base_string_snippet, extension_string_val.chars().next().unwrap()); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 923b319d777..ffab0395120 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -84,6 +84,7 @@ pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; 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"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..000ab8b8f36 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2012,6 +2012,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "single_char_push_str", + group: "style", + desc: "`push_str()` used with a single-character string literal as parameter", + deprecation: None, + module: "single_char_push_str", + }, Lint { name: "single_component_path_imports", group: "style", diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed new file mode 100644 index 00000000000..49607c49218 --- /dev/null +++ b/tests/ui/single_char_push_str.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); +} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs new file mode 100644 index 00000000000..bbeebd027b1 --- /dev/null +++ b/tests/ui/single_char_push_str.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); +} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr new file mode 100644 index 00000000000..453ec2d42f1 --- /dev/null +++ b/tests/ui/single_char_push_str.stderr @@ -0,0 +1,16 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:6:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:7:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: aborting due to 2 previous errors + From ae56e988a2ae7b59c684cbbc5c326cb8097b3688 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 14 Aug 2020 12:52:19 +0200 Subject: [PATCH 0439/1110] Merge lint with `single_char_pattern` --- clippy_lints/src/lib.rs | 8 +-- clippy_lints/src/methods/mod.rs | 89 ++++++++++++++++++++---- clippy_lints/src/single_char_push_str.rs | 62 ----------------- src/lintlist/mod.rs | 2 +- tests/ui/single_char_push_str.fixed | 5 ++ tests/ui/single_char_push_str.rs | 5 ++ tests/ui/single_char_push_str.stderr | 20 +++++- 7 files changed, 108 insertions(+), 83 deletions(-) delete mode 100644 clippy_lints/src/single_char_push_str.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4f261ba932e..5e4a4a4f49c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,7 +287,6 @@ mod repeat_once; mod returns; mod serde_api; mod shadow; -mod single_char_push_str; mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; @@ -678,6 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, + &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -776,7 +776,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, - &single_char_push_str::SINGLE_CHAR_PUSH_STR, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, @@ -934,7 +933,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); - store.register_late_pass(|| box single_char_push_str::SingleCharPushStrPass); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box vec::UselessVec); @@ -1352,6 +1350,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1419,7 +1418,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), - LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), @@ -1536,6 +1534,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::WRONG_SELF_CONVENTION), @@ -1560,7 +1559,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6266ef2ba1..2986a5a5944 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1306,6 +1306,29 @@ declare_clippy_lint! { "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" } +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** it's less clear that we are pushing a single character + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1327,6 +1350,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, + SINGLE_CHAR_PUSH_STR, SEARCH_IS_SOME, TEMPORARY_CSTRING_AS_PTR, FILTER_NEXT, @@ -1441,6 +1465,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::lint(cx, expr, &args[0], self_ty); } + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + lint_single_char_push_string(cx, expr, args); + } + } + match self_ty.kind { ty::Ref(_, ty, _) if ty.kind == ty::Str => { for &(method, pos) in &PATTERN_METHODS { @@ -3124,15 +3154,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn get_hint_if_single_char_arg<'tcx>( + cx: &LateContext<'tcx>, + arg: &'tcx hir::Expr<'_>, + applicability: &mut Applicability, +) -> Option { if_chain! { if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; - if r.as_str().len() == 1; + let string = r.as_str(); + if string.len() == 1; then { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## @@ -3142,19 +3175,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr &snip[1..(snip.len() - 1)] }; let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); + Some(hint) + } else { + None } } } +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } +} + +/// lint for length-1 `str`s as argument for `push_str` +fn lint_single_char_push_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/single_char_push_str.rs b/clippy_lints/src/single_char_push_str.rs deleted file mode 100644 index 68bbef7261a..00000000000 --- a/clippy_lints/src/single_char_push_str.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Warns when using push_str with a single-character string literal, - /// and push with a char would work fine. - /// - /// **Why is this bad?** This is in all probability not the intended outcome. At - /// the least it hurts readability of the code. - /// - /// **Known problems:** None - /// - /// **Example:** - /// ``` - /// let mut string = String::new(); - /// string.push_str("R"); - /// ``` - /// Could be written as - /// ``` - /// let mut string = String::new(); - /// string.push('R'); - /// ``` - pub SINGLE_CHAR_PUSH_STR, - style, - "`push_str()` used with a single-character string literal as parameter" -} - -declare_lint_pass!(SingleCharPushStrPass => [SINGLE_CHAR_PUSH_STR]); - -impl<'tcx> LateLintPass<'tcx> for SingleCharPushStrPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; - if let [base_string, extension_string] = args; - if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, fn_def_id, &paths::PUSH_STR); - if let ExprKind::Lit(ref lit) = extension_string.kind; - if let LitKind::Str(symbol,_) = lit.node; - let extension_string_val = symbol.as_str().to_string(); - if extension_string_val.len() == 1; - then { - let mut applicability = Applicability::MachineApplicable; - let base_string_snippet = snippet_with_applicability(cx, base_string.span, "_", &mut applicability); - let sugg = format!("{}.push({:?})", base_string_snippet, extension_string_val.chars().next().unwrap()); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PUSH_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability - ); - } - } - } -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 000ab8b8f36..4fd32776874 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2017,7 +2017,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "`push_str()` used with a single-character string literal as parameter", deprecation: None, - module: "single_char_push_str", + module: "methods", }, Lint { name: "single_component_path_imports", diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index 49607c49218..0812c026a64 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -7,4 +7,9 @@ fn main() { string.push('\''); string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); } diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs index bbeebd027b1..ab293bbe4ee 100644 --- a/tests/ui/single_char_push_str.rs +++ b/tests/ui/single_char_push_str.rs @@ -7,4 +7,9 @@ fn main() { string.push_str("'"); string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 453ec2d42f1..0e9bdaa23e7 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -12,5 +12,23 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str("'"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` -error: aborting due to 2 previous errors +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:12:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:13:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:14:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: aborting due to 5 previous errors From b381ade1795f36149e36a646cdc83ee2fff032bf Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Sat, 15 Aug 2020 01:40:47 +0200 Subject: [PATCH 0440/1110] elide lifetimes --- clippy_lints/src/methods/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2986a5a5944..614773a7e26 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1310,7 +1310,7 @@ declare_clippy_lint! { /// **What it does:** Warns when using push_str with a single-character string literal, /// and push with a char would work fine. /// - /// **Why is this bad?** it's less clear that we are pushing a single character + /// **Why is this bad?** It's less clear that we are pushing a single character /// /// **Known problems:** None /// @@ -3154,9 +3154,9 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -fn get_hint_if_single_char_arg<'tcx>( - cx: &LateContext<'tcx>, - arg: &'tcx hir::Expr<'_>, +fn get_hint_if_single_char_arg( + cx: &LateContext<'_>, + arg: &hir::Expr<'_>, applicability: &mut Applicability, ) -> Option { if_chain! { @@ -3183,7 +3183,7 @@ fn get_hint_if_single_char_arg<'tcx>( } /// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let mut applicability = Applicability::MachineApplicable; if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { span_lint_and_sugg( @@ -3199,7 +3199,7 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr } /// lint for length-1 `str`s as argument for `push_str` -fn lint_single_char_push_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { +fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); From 6d18fe730e88903fa881f7b753369623108a5d55 Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 12 Aug 2020 15:43:44 +0300 Subject: [PATCH 0441/1110] Make needless_return a late lint pass --- clippy_lints/src/lib.rs | 8 +- clippy_lints/src/needless_return.rs | 166 ++++++++++++++++++++++++++++ clippy_lints/src/returns.rs | 141 +---------------------- src/lintlist/mod.rs | 2 +- 4 files changed, 175 insertions(+), 142 deletions(-) create mode 100644 clippy_lints/src/needless_return.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..d2f66cf9bd0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -256,6 +256,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; +mod needless_return; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -726,6 +727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + &needless_return::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -769,7 +771,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, @@ -1027,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_early_pass(|| box returns::Return); store.register_late_pass(|| box let_and_return::LetReturn); + store.register_late_pass(|| box needless_return::NeedlessReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1381,6 +1383,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_return::NEEDLESS_RETURN), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1413,7 +1416,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), @@ -1543,6 +1545,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&needless_return::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1554,7 +1557,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs new file mode 100644 index 00000000000..a8876619ac1 --- /dev/null +++ b/clippy_lints/src/needless_return.rs @@ -0,0 +1,166 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_ast::ast::Attribute; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_span::source_map::Span; +use rustc_middle::lint::in_external_macro; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; + +use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for return statements at the end of a block. + /// + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// **Known problems:** If the computation returning the value borrows a local + /// variable, removing the `return` may run afoul of the borrow checker. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +#[derive(PartialEq, Eq, Copy, Clone)] +enum RetReplacement { + Empty, + Block, +} + +declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); + +impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { + fn check_fn(&mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, _: Span, _: HirId) { + match kind { + FnKind::Closure(_) => { + check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) + } + FnKind::ItemFn(..) | FnKind::Method(..) => { + if let ExprKind::Block(ref block, _) = body.value.kind { + check_block_return(cx, block) + } + } + } + } +} + +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} + +fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { + if let Some(expr) = block.expr { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + } + _ => (), + } + } +} + + +fn check_final_expr( + cx: &LateContext<'_>, + expr: &Expr<'_>, + span: Option, + replacement: RetReplacement, +) { + match expr.kind { + // simple return is always "bad" + ExprKind::Ret(ref inner) => { + + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + emit_return_lint( + cx, + span.expect("`else return` is not possible"), + inner.as_ref().map(|i| i.span), + replacement, + ); + } + } + // a whole block? check it! + ExprKind::Block(ref block, _) => { + check_block_return(cx, block); + } + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + + ExprKind::Match(_, ref arms, source) => { + match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } + } + MatchSource::IfDesugar { contains_else_clause: true } | MatchSource::IfLetDesugar { contains_else_clause: true } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + } + _ => () + } + } + _ => (), + } +} + +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { + return; + } + + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } + }) + } + None => match replacement { + RetReplacement::Empty => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", + String::new(), + Applicability::MachineApplicable, + ); + } + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + } + }, + } +} diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8ed20995a70..2bd0cccd39d 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -3,38 +3,11 @@ use rustc_ast::ast; use rustc_ast::visit::FnKind; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for return statements at the end of a block. - /// - /// **Why is this bad?** Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// **Known problems:** If the computation returning the value borrows a local - /// variable, removing the `return` may run afoul of the borrow checker. - /// - /// **Example:** - /// ```rust - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```rust - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - pub NEEDLESS_RETURN, - style, - "using a return statement like `return expr;` where an expression would suffice" -} +use crate::utils::{span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -57,117 +30,12 @@ declare_clippy_lint! { "needless unit expression" } -#[derive(PartialEq, Eq, Copy, Clone)] -enum RetReplacement { - Empty, - Block, -} -declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]); - -impl Return { - // Check the final stmt or expr in a block for unnecessary return. - fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => { - self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), - } - } - } - - // Check the final expression in a block if it's a return. - fn check_final_expr( - &mut self, - cx: &EarlyContext<'_>, - expr: &ast::Expr, - span: Option, - replacement: RetReplacement, - ) { - match expr.kind { - // simple return is always "bad" - ast::ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - Self::emit_return_lint( - cx, - span.expect("`else return` is not possible"), - inner.as_ref().map(|i| i.span), - replacement, - ); - } - }, - // a whole block? check it! - ast::ExprKind::Block(ref block, _) => { - self.check_block_return(cx, block); - }, - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => { - self.check_block_return(cx, ifblock); - self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty); - }, - // a match expr, check all arms - ast::ExprKind::Match(_, ref arms) => { - for arm in arms { - self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - _ => (), - } - } - - fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() { - return; - } - - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - }, - } - } -} +declare_lint_pass!(Return => [UNUSED_UNIT]); impl EarlyLintPass for Return { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - match kind { - FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block), - FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty), - FnKind::Fn(.., None) => {}, - } + if_chain! { if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; if let ast::TyKind::Tup(ref vals) = ty.kind; @@ -234,9 +102,6 @@ impl EarlyLintPass for Return { } } -fn attr_is_cfg(attr: &ast::Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) -} // get the def site #[must_use] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..7464f1cc6de 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1534,7 +1534,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "using a return statement like `return expr;` where an expression would suffice", deprecation: None, - module: "returns", + module: "needless_return", }, Lint { name: "needless_update", From 85f4ef0fbd92453cf480af4e3f9eed877071ea2e Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 12 Aug 2020 17:14:12 +0300 Subject: [PATCH 0442/1110] Visitor added --- clippy_lints/src/needless_return.rs | 45 +++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index a8876619ac1..861a7ec558c 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -2,12 +2,14 @@ use rustc_lint::{LateLintPass, LateContext}; use rustc_ast::ast::Attribute; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; +use rustc_hir::intravisit::{FnKind, walk_expr, NestedVisitorMap, Visitor}; use rustc_span::source_map::Span; use rustc_middle::lint::in_external_macro; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::subst::GenericArgKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for return statements at the end of a block. @@ -164,3 +166,42 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + From 65d10c7abf4752923f040264c79433da8fc234ea Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 15:14:08 +0300 Subject: [PATCH 0443/1110] Borrow checker added --- clippy_lints/src/needless_return.rs | 101 ++++++++++++++++------------ clippy_lints/src/returns.rs | 5 +- tests/ui/needless_return.fixed | 10 +++ tests/ui/needless_return.rs | 10 +++ 4 files changed, 80 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index 861a7ec558c..ba280cd5a61 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -1,13 +1,13 @@ -use rustc_lint::{LateLintPass, LateContext}; use rustc_ast::ast::Attribute; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, walk_expr, NestedVisitorMap, Visitor}; -use rustc_span::source_map::Span; -use rustc_middle::lint::in_external_macro; -use rustc_middle::hir::map::Map; -use rustc_middle::ty::subst::GenericArgKind; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; @@ -46,16 +46,39 @@ enum RetReplacement { declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { - fn check_fn(&mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, _: Span, _: HirId) { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: HirId, + ) { match kind { FnKind::Closure(_) => { - check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) - } + if !last_statement_borrows(cx, &body.value) { + check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) + } + }, FnKind::ItemFn(..) | FnKind::Method(..) => { if let ExprKind::Block(ref block, _) = body.value.kind { - check_block_return(cx, block) + if let Some(expr) = block.expr { + if !last_statement_borrows(cx, expr) { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + if !last_statement_borrows(cx, expr) { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + } + }, + _ => (), + } + } } - } + }, } } } @@ -71,23 +94,16 @@ fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { match stmt.kind { StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - } + }, _ => (), } } } - -fn check_final_expr( - cx: &LateContext<'_>, - expr: &Expr<'_>, - span: Option, - replacement: RetReplacement, -) { +fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option, replacement: RetReplacement) { match expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` if !expr.attrs.iter().any(attr_is_cfg) { emit_return_lint( @@ -97,32 +113,34 @@ fn check_final_expr( replacement, ); } - } + }, // a whole block? check it! ExprKind::Block(ref block, _) => { check_block_return(cx, block); - } + }, // a match expr, check all arms // an if/if let expr, check both exprs // note, if without else is going to be a type checking error anyways // (except for unit type functions) so we don't match it - - ExprKind::Match(_, ref arms, source) => { - match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } + ExprKind::Match(_, ref arms, source) => match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); } - MatchSource::IfDesugar { contains_else_clause: true } | MatchSource::IfLetDesugar { contains_else_clause: true } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { - check_block_return(cx, ifblock); - } - check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); - } - _ => () + }, + MatchSource::IfDesugar { + contains_else_clause: true, } - } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + }, + _ => (), + }, _ => (), } } @@ -139,7 +157,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option match replacement { RetReplacement::Empty => { span_lint_and_sugg( @@ -151,7 +169,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { span_lint_and_sugg( cx, @@ -162,7 +180,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { NestedVisitorMap::None } } - diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2bd0cccd39d..593e2f6c74b 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -30,12 +30,10 @@ declare_clippy_lint! { "needless unit expression" } - declare_lint_pass!(Return => [UNUSED_UNIT]); impl EarlyLintPass for Return { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; if let ast::TyKind::Tup(ref vals) = ty.kind; @@ -102,7 +100,6 @@ impl EarlyLintPass for Return { } } - // get the def site #[must_use] fn get_def(span: Span) -> Option { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ad20e238107..6b5cf2626df 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,6 +69,16 @@ fn test_void_match(x: u32) { } } +mod no_lint_if_stmt_borrows { + mod issue_5858 { + fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); + } + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index af0cdfb207f..1a693c9aa53 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,6 +69,16 @@ fn test_void_match(x: u32) { } } +mod no_lint_if_stmt_borrows { + mod issue_5858 { + fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); + } + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); From cd6ca72e0e4040a5c3a6623c2e8533db91db6042 Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 15:21:09 +0300 Subject: [PATCH 0444/1110] Known problems changed --- clippy_lints/src/needless_return.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index ba280cd5a61..eb0bf12c0ab 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -17,8 +17,7 @@ declare_clippy_lint! { /// **Why is this bad?** Removing the `return` and semicolon will make the code /// more rusty. /// - /// **Known problems:** If the computation returning the value borrows a local - /// variable, removing the `return` may run afoul of the borrow checker. + /// **Known problems:** None. /// /// **Example:** /// ```rust From a7d5c2f967dd1f075ba5f8c4ca05c4b2ca2d22b4 Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 19:24:34 +0300 Subject: [PATCH 0445/1110] Modifications according to the code review --- clippy_lints/src/let_and_return.rs | 124 ---------- clippy_lints/src/lib.rs | 26 +- clippy_lints/src/needless_return.rs | 223 ------------------ clippy_lints/src/returns.rs | 353 ++++++++++++++++++++-------- clippy_lints/src/unused_unit.rs | 145 ++++++++++++ tests/ui/needless_return.fixed | 11 + tests/ui/needless_return.rs | 11 + 7 files changed, 428 insertions(+), 465 deletions(-) delete mode 100644 clippy_lints/src/let_and_return.rs delete mode 100644 clippy_lints/src/needless_return.rs create mode 100644 clippy_lints/src/unused_unit.rs diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs deleted file mode 100644 index fa560ffb980..00000000000 --- a/clippy_lints/src/let_and_return.rs +++ /dev/null @@ -1,124 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_lint_pass!(LetReturn => [LET_AND_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for LetReturn { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d2f66cf9bd0..63de0f8a0c2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -218,7 +218,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -256,7 +255,6 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; -mod needless_return; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -311,6 +309,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; +mod unused_unit; mod unwrap; mod use_self; mod useless_conversion; @@ -587,7 +586,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &let_and_return::LET_AND_RETURN, + &returns::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -727,7 +726,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &needless_return::NEEDLESS_RETURN, + &returns::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -771,7 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &returns::UNUSED_UNIT, + &unused_unit::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1026,9 +1025,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box misc_early::MiscEarlyLints); store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); - store.register_early_pass(|| box returns::Return); - store.register_late_pass(|| box let_and_return::LetReturn); - store.register_late_pass(|| box needless_return::NeedlessReturn); + store.register_early_pass(|| box unused_unit::UnusedUnit); + store.register_late_pass(|| box returns::Return); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1286,7 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1383,7 +1381,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_return::NEEDLESS_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1416,7 +1414,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1502,7 +1500,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1545,7 +1543,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&needless_return::NEEDLESS_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1557,7 +1555,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs deleted file mode 100644 index eb0bf12c0ab..00000000000 --- a/clippy_lints/src/needless_return.rs +++ /dev/null @@ -1,223 +0,0 @@ -use rustc_ast::ast::Attribute; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for return statements at the end of a block. - /// - /// **Why is this bad?** Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```rust - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - pub NEEDLESS_RETURN, - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -#[derive(PartialEq, Eq, Copy, Clone)] -enum RetReplacement { - Empty, - Block, -} - -declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - _: Span, - _: HirId, - ) { - match kind { - FnKind::Closure(_) => { - if !last_statement_borrows(cx, &body.value) { - check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) - } - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(ref block, _) = body.value.kind { - if let Some(expr) = block.expr { - if !last_statement_borrows(cx, expr) { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { - if !last_statement_borrows(cx, expr) { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - } - }, - _ => (), - } - } - } - }, - } - } -} - -fn attr_is_cfg(attr: &Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) -} - -fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { - if let Some(expr) = block.expr { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), - } - } -} - -fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option, replacement: RetReplacement) { - match expr.kind { - // simple return is always "bad" - ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - emit_return_lint( - cx, - span.expect("`else return` is not possible"), - inner.as_ref().map(|i| i.span), - replacement, - ); - } - }, - // a whole block? check it! - ExprKind::Block(ref block, _) => { - check_block_return(cx, block); - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, ref arms, source) => match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - MatchSource::IfDesugar { - contains_else_clause: true, - } - | MatchSource::IfLetDesugar { - contains_else_clause: true, - } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { - check_block_return(cx, ifblock); - } - check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); - }, - _ => (), - }, - _ => (), - } -} - -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { - return; - } - - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - }, - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 593e2f6c74b..4d91f9be999 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,145 +1,290 @@ use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; -use crate::utils::span_lint_and_sugg; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { - /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. /// - /// **Why is this bad?** Such expressions add no value, but can make the code - /// less readable. Depending on formatting they can make a `break` or `return` - /// statement look like a function call. + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust - /// fn return_unit() -> () { - /// () + /// fn foo() -> String { + /// let x = String::new(); + /// x /// } /// ``` - pub UNUSED_UNIT, + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, style, - "needless unit expression" + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" } -declare_lint_pass!(Return => [UNUSED_UNIT]); +declare_clippy_lint! { + /// **What it does:** Checks for return statements at the end of a block. + /// + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, + style, + "using a return statement like `return expr;` where an expression would suffice" +} -impl EarlyLintPass for Return { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - lint_unneeded_unit_return(cx, ty, span); - } - } - } +#[derive(PartialEq, Eq, Copy, Clone)] +enum RetReplacement { + Empty, + Block, +} - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); + +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr if_chain! { - if let Some(ref stmt) = block.stmts.last(); - if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); then { - let sp = expr.span; - span_lint_and_sugg( + span_lint_and_then( cx, - UNUSED_UNIT, - sp, - "unneeded unit expression", - "remove the final `()`", - String::new(), - Applicability::MachineApplicable, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(snippet) = snippet_opt(cx, initexpr.span) { + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, ); } } } - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: HirId, + ) { + match kind { + FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), + FnKind::ItemFn(..) | FnKind::Method(..) => { + if let ExprKind::Block(ref block, _) = body.value.kind { + check_block_return(cx, block); } }, + } + } +} + +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} + +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { + if let Some(expr) = block.expr { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + }, _ => (), } } +} - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { - let segments = &poly.trait_ref.path.segments; +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + span: Option, + replacement: RetReplacement, +) { + if last_statement_borrows(cx, expr) { + return; + } - if_chain! { - if segments.len() == 1; - if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); - if let Some(args) = &segments[0].args; - if let ast::GenericArgs::Parenthesized(generic_args) = &**args; - if let ast::FnRetTy::Ty(ty) = &generic_args.output; - if ty.kind.is_unit(); - then { - lint_unneeded_unit_return(cx, ty, generic_args.span); + match expr.kind { + // simple return is always "bad" + ExprKind::Ret(ref inner) => { + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if !borrows { + emit_return_lint( + cx, + span.expect("`else return` is not possible"), + inner.as_ref().map(|i| i.span), + replacement, + ); + } } - } + }, + // a whole block? check it! + ExprKind::Block(ref block, _) => { + check_block_return(cx, block); + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, ref arms, source) => match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } + }, + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + }, + _ => (), + }, + _ => (), } } -// get the def site -#[must_use] -fn get_def(span: Span) -> Option { - if span.from_expansion() { - Some(span.ctxt().outer_expn_data().def_site) - } else { - None - } -} +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { + return; + } -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false - } -} - -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { - let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source - .rfind("->") - .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } }) - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - ret_span, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + }, + None => match replacement { + RetReplacement::Empty => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", + String::new(), + Applicability::MachineApplicable, + ); + }, + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + }, + }, + } +} + +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs new file mode 100644 index 00000000000..e322e402535 --- /dev/null +++ b/clippy_lints/src/unused_unit.rs @@ -0,0 +1,145 @@ +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::span_lint_and_sugg; + +declare_clippy_lint! { + /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// + /// **Why is this bad?** Such expressions add no value, but can make the code + /// less readable. Depending on formatting they can make a `break` or `return` + /// statement look like a function call. + /// + /// **Known problems:** The lint currently misses unit return types in types, + /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// + /// **Example:** + /// ```rust + /// fn return_unit() -> () { + /// () + /// } + /// ``` + pub UNUSED_UNIT, + style, + "needless unit expression" +} + +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); + +impl EarlyLintPass for UnusedUnit { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { + if_chain! { + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; + if let ast::TyKind::Tup(ref vals) = ty.kind; + if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); + then { + lint_unneeded_unit_return(cx, ty, span); + } + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + if_chain! { + if let Some(ref stmt) = block.stmts.last(); + if let ast::StmtKind::Expr(ref expr) = stmt.kind; + if is_unit_expr(expr) && !stmt.span.from_expansion(); + then { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + match e.kind { + ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { + if is_unit_expr(expr) && !expr.span.from_expansion() { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } +} + +// get the def site +#[must_use] +fn get_def(span: Span) -> Option { + if span.from_expansion() { + Some(span.ctxt().outer_expn_data().def_site) + } else { + None + } +} + +// is this expr a `()` unit? +fn is_unit_expr(expr: &ast::Expr) -> bool { + if let ast::ExprKind::Tup(ref vals) = expr.kind { + vals.is_empty() + } else { + false + } +} + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 6b5cf2626df..b795516f999 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -76,6 +76,17 @@ mod no_lint_if_stmt_borrows { let stdin = ::std::io::stdin(); return stdin.lock().lines().next().unwrap().unwrap(); } + + fn read_line2(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } + } } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 1a693c9aa53..3547991935d 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -76,6 +76,17 @@ mod no_lint_if_stmt_borrows { let stdin = ::std::io::stdin(); return stdin.lock().lines().next().unwrap().unwrap(); } + + fn read_line2(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } + } } } From 96efaee55240a3d3428ea5fe30c884a3227dad6e Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 19:30:49 +0300 Subject: [PATCH 0446/1110] cargo dev update_lints --- clippy_lints/src/lib.rs | 18 +++++++++--------- src/lintlist/mod.rs | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 63de0f8a0c2..c0b645e9311 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -586,7 +586,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &returns::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -726,7 +725,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &returns::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -770,7 +768,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &unused_unit::UNUSED_UNIT, + &returns::LET_AND_RETURN, + &returns::NEEDLESS_RETURN, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -840,6 +839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, + &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, &use_self::USE_SELF, @@ -1284,7 +1284,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1381,7 +1380,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1414,7 +1412,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&unused_unit::UNUSED_UNIT), + LintId::of(&returns::LET_AND_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1460,6 +1459,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), @@ -1500,7 +1500,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1543,7 +1542,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1555,7 +1553,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&unused_unit::UNUSED_UNIT), + LintId::of(&returns::LET_AND_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), @@ -1564,6 +1563,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 7464f1cc6de..dff6198d00d 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1037,7 +1037,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", deprecation: None, - module: "let_and_return", + module: "returns", }, Lint { name: "let_underscore_lock", @@ -1534,7 +1534,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "using a return statement like `return expr;` where an expression would suffice", deprecation: None, - module: "needless_return", + module: "returns", }, Lint { name: "needless_update", @@ -2479,7 +2479,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "needless unit expression", deprecation: None, - module: "returns", + module: "unused_unit", }, Lint { name: "unwrap_used", From baa4cb1cddc3a8ce1f47c4006e236edf082ee858 Mon Sep 17 00:00:00 2001 From: jrqc Date: Fri, 14 Aug 2020 09:25:26 +0300 Subject: [PATCH 0447/1110] early return removed --- clippy_lints/src/returns.rs | 4 ---- clippy_lints/src/unused_unit.rs | 3 +-- tests/ui/needless_return.fixed | 30 +++++++++++++----------------- tests/ui/needless_return.rs | 30 +++++++++++++----------------- tests/ui/needless_return.stderr | 14 +++++++++++++- 5 files changed, 40 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4d91f9be999..3c5541e64b4 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -160,10 +160,6 @@ fn check_final_expr<'tcx>( span: Option, replacement: RetReplacement, ) { - if last_statement_borrows(cx, expr) { - return; - } - match expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index e322e402535..7548c6afa97 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -16,8 +16,7 @@ declare_clippy_lint! { /// less readable. Depending on formatting they can make a `break` or `return` /// statement look like a function call. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index b795516f999..d849e093da7 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,24 +69,20 @@ fn test_void_match(x: u32) { } } -mod no_lint_if_stmt_borrows { - mod issue_5858 { - fn read_line() -> String { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - return stdin.lock().lines().next().unwrap().unwrap(); - } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} - fn read_line2(value: bool) -> String { - if value { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - let _a = stdin.lock().lines().next().unwrap().unwrap(); - return String::from("test"); - } else { - return String::new(); - } - } +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + String::from("test") + } else { + String::new() } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 3547991935d..29f2bd1852a 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,24 +69,20 @@ fn test_void_match(x: u32) { } } -mod no_lint_if_stmt_borrows { - mod issue_5858 { - fn read_line() -> String { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - return stdin.lock().lines().next().unwrap().unwrap(); - } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} - fn read_line2(value: bool) -> String { - if value { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - let _a = stdin.lock().lines().next().unwrap().unwrap(); - return String::from("test"); - } else { - return String::new(); - } - } +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); } } diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index c34eecbcbb6..f73c833a801 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -72,5 +72,17 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` -error: aborting due to 12 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:83:9 + | +LL | return String::from("test"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:85:9 + | +LL | return String::new(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + +error: aborting due to 14 previous errors From 1a140dcc1c933ae84365bd1153372a7430f6f647 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 16 Aug 2020 00:25:54 +0200 Subject: [PATCH 0448/1110] Improve needless_doctest_main by using the parser --- clippy_lints/src/doc.rs | 75 ++++++++++++++++++++++++++++--- tests/ui/needless_doc_main.rs | 68 ++++++++++++++++++++++++++-- tests/ui/needless_doc_main.stderr | 12 +++-- 3 files changed, 142 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 6ce36fd2360..9555459e240 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,16 +1,22 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{AttrKind, Attribute}; +use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::EmitterWriter; +use rustc_errors::Handler; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; +use rustc_parse::maybe_new_parser_from_source_str; +use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{BytePos, MultiSpan, Span}; -use rustc_span::Pos; +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::{FileName, Pos}; +use std::io; use std::ops::Range; use url::Url; @@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { + fn has_needless_main(code: &str) -> bool { + let filename = FileName::anon_source_code(code); + + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); + } + return false; + }, + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + 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(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + } + + if has_needless_main(text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 682d7b3c4ce..883683e08a2 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -9,8 +9,14 @@ /// } /// ``` /// -/// This should, too. +/// With an explicit return type it should lint too +/// ``` +/// fn main() -> () { +/// unimplemented!(); +/// } +/// ``` /// +/// This should, too. /// ```rust /// fn main() { /// unimplemented!(); @@ -18,7 +24,6 @@ /// ``` /// /// This one too. -/// /// ```no_run /// fn main() { /// unimplemented!(); @@ -33,6 +38,20 @@ fn bad_doctests() {} /// fn main(){} /// ``` /// +/// This shouldn't lint either, because main is async: +/// ``` +/// async fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Same here, because the return type is not the unit type: +/// ``` +/// fn main() -> Result<()> { +/// Ok(()) +/// } +/// ``` +/// /// This shouldn't lint either, because there's a `static`: /// ``` /// static ANSWER: i32 = 42; @@ -42,6 +61,15 @@ fn bad_doctests() {} /// } /// ``` /// +/// This shouldn't lint either, because there's a `const`: +/// ``` +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// +/// const ANSWER: i32 = 42; +/// ``` +/// /// Neither should this lint because of `extern crate`: /// ``` /// #![feature(test)] @@ -51,8 +79,41 @@ fn bad_doctests() {} /// } /// ``` /// -/// We should not lint ignored examples: +/// Neither should this lint because it has an extern block: +/// ``` +/// extern {} +/// fn main() { +/// unimplemented!(); +/// } +/// ``` /// +/// This should not lint because there is another function defined: +/// ``` +/// fn fun() {} +/// +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// We should not lint inside raw strings ... +/// ``` +/// let string = r#" +/// fn main() { +/// unimplemented!(); +/// } +/// "#; +/// ``` +/// +/// ... or comments +/// ``` +/// // fn main() { +/// // let _inception = 42; +/// // } +/// let _inception = 42; +/// ``` +/// +/// We should not lint ignored examples: /// ```rust,ignore /// fn main() { /// unimplemented!(); @@ -60,7 +121,6 @@ fn bad_doctests() {} /// ``` /// /// Or even non-rust examples: -/// /// ```text /// fn main() { /// is what starts the program diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 65d40ee6832..05c7f9d33a7 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -7,16 +7,22 @@ LL | /// fn main() { = note: `-D clippy::needless-doctest-main` implied by `-D warnings` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:15:4 + --> $DIR/needless_doc_main.rs:14:4 + | +LL | /// fn main() -> () { + | ^^^^^^^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:21:4 | LL | /// fn main() { | ^^^^^^^^^^^^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:23:4 + --> $DIR/needless_doc_main.rs:28:4 | LL | /// fn main() { | ^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From a3ea65c2d9bc52f308745ae488435388fce753a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 15 Jun 2020 11:21:56 +0200 Subject: [PATCH 0449/1110] Implement new lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/mod.rs | 130 ++++++++++++++++++++++- src/lintlist/mod.rs | 7 ++ tests/ui/unnecessary_lazy_eval.fixed | 105 +++++++++++++++++++ tests/ui/unnecessary_lazy_eval.rs | 105 +++++++++++++++++++ tests/ui/unnecessary_lazy_eval.stderr | 144 ++++++++++++++++++++++++++ 7 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_lazy_eval.fixed create mode 100644 tests/ui/unnecessary_lazy_eval.rs create mode 100644 tests/ui/unnecessary_lazy_eval.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 50fe7612909..c21190e9b9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1646,6 +1646,7 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`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 +[`option_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_or_else [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 986e9d9bee4..cb29f71387b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -672,6 +672,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, + &methods::UNNECESSARY_LAZY_EVALUATION, &methods::OR_FUN_CALL, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, @@ -1360,6 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1610,6 +1612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2330978e67f..61b7f2647ee 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1329,6 +1329,32 @@ declare_clippy_lint! { "`push_str()` used with a single-character string literal as parameter" } +declare_clippy_lint! { + /// **What it does:** Looks for unnecessary lazily evaluated closures on `Option` and `Result`. + /// + /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let opt: Option = None; + /// + /// opt.unwrap_or_else(|| 42); + /// ``` + /// Use instead: + /// ```rust + /// let opt: Option = None; + /// + /// opt.unwrap_or(42); + /// ``` + pub UNNECESSARY_LAZY_EVALUATION, + style, + "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1378,6 +1404,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, + UNNECESSARY_LAZY_EVALUATION, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1398,13 +1425,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), + ["unwrap_or_else", "map"] => { + lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"); + lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]); + }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { + lint_lazy_eval(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { + lint_lazy_eval(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1448,6 +1480,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["unwrap_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -2663,6 +2698,99 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +fn lint_lazy_eval<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(result_type)); + + if !is_option && !is_result { + return; + } + + // Return true if the expression is an accessor of any of the arguments + fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) + } + + fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + let simplify = match ex.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(ex, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if allow_variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + !args.iter().any(|arg| expr_uses_argument(arg, params)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + }; + + if simplify { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATION, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } +} + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 233f95deedd..1e76163f946 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2383,6 +2383,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "unnecessary_lazy_eval", + group: "style", + desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", + deprecation: None, + module: "methods", + }, Lint { name: "unnecessary_mut_passed", group: "style", diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed new file mode 100644 index 00000000000..fcfa6dfe12d --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -0,0 +1,105 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluation)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: u32, +} + +impl SomeStruct { + fn return_some_field(&self) -> u32 { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_str = SomeStruct { some_field: 10 }; + + // Should lint - Option + let mut opt = Some(42); + let ext_opt = Some(42); + let _ = opt.unwrap_or(2); + let _ = opt.unwrap_or(astronomers_pi); + let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.and(ext_opt); + let _ = opt.or(ext_opt); + let _ = opt.or(None); + let _ = opt.get_or_insert(2); + let _ = opt.ok_or(2); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or(2); + let _ = Some(10).and(ext_opt); + let _: Option = None.or(ext_opt); + let _ = None.get_or_insert(2); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or(2); + let _ = deep.0.and(ext_opt); + let _ = deep.0.or(None); + let _ = deep.0.get_or_insert(2); + let _ = deep.0.ok_or(2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or(2); + let _ = res2.unwrap_or(astronomers_pi); + let _ = res2.unwrap_or(ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..04b3c8ae1e2 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -0,0 +1,105 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_eval)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: u32, +} + +impl SomeStruct { + fn return_some_field(&self) -> u32 { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_str = SomeStruct { some_field: 10 }; + + // Should lint - Option + let mut opt = Some(42); + let ext_opt = Some(42); + let _ = opt.unwrap_or_else(|| 2); + let _ = opt.unwrap_or_else(|| astronomers_pi); + let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.and_then(|_| ext_opt); + let _ = opt.or_else(|| ext_opt); + let _ = opt.or_else(|| None); + let _ = opt.get_or_insert_with(|| 2); + let _ = opt.ok_or_else(|| 2); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or_else(|| 2); + let _ = Some(10).and_then(|_| ext_opt); + let _: Option = None.or_else(|| ext_opt); + let _ = None.get_or_insert_with(|| 2); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or_else(|| 2); + let _ = deep.0.and_then(|_| ext_opt); + let _ = deep.0.or_else(|| None); + let _ = deep.0.get_or_insert_with(|| 2); + let _ = deep.0.ok_or_else(|| 2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or_else(|_| 2); + let _ = res2.unwrap_or_else(|_| astronomers_pi); + let _ = res2.unwrap_or_else(|_| ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr new file mode 100644 index 00000000000..b941bf84246 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -0,0 +1,144 @@ +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:30:13 + | +LL | let _ = opt.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:31:13 + | +LL | let _ = opt.unwrap_or_else(|| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:32:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:33:13 + | +LL | let _ = opt.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 + | +LL | let _ = opt.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:36:13 + | +LL | let _ = opt.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:37:13 + | +LL | let _ = opt.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:40:13 + | +LL | let _ = Some(10).unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:41:13 + | +LL | let _ = Some(10).and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:42:26 + | +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | +LL | let _ = None.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:44:31 + | +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:45:26 + | +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:48:13 + | +LL | let _ = deep.0.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:49:13 + | +LL | let _ = deep.0.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:50:13 + | +LL | let _ = deep.0.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:51:13 + | +LL | let _ = deep.0.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:52:13 + | +LL | let _ = deep.0.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:73:13 + | +LL | let _ = res2.unwrap_or_else(|_| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:74:13 + | +LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:75:13 + | +LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + +error: unknown clippy lint: clippy::unnecessary_lazy_eval + --> $DIR/unnecessary_lazy_eval.rs:2:9 + | +LL | #![warn(clippy::unnecessary_lazy_eval)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_lazy_evaluation` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 23 previous errors + From 923d61222cc9de9e662e90570e4725ef00f48d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 15 Jun 2020 11:48:14 +0200 Subject: [PATCH 0450/1110] Rename the changelog footnote as well --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c21190e9b9c..54489751014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1646,7 +1646,6 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`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 -[`option_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_or_else [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`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 @@ -1755,6 +1754,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold +[`unnecessary_lazy_eval`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_eval [`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 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by From 848af393103d769458a206547ea4506fd0229304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 15 Jun 2020 14:55:23 +0200 Subject: [PATCH 0451/1110] Add note to `or_fun_call`, list checked methods --- clippy_lints/src/methods/mod.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 61b7f2647ee..c672ca41dec 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1330,11 +1330,21 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Looks for unnecessary lazily evaluated closures on `Option` and `Result`. + /// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary + /// lazily evaluated closures on `Option` and `Result`. + /// + /// This lint suggests changing the following functions, when eager evaluation results in + /// simpler code: + /// - `unwrap_or_else` to `unwrap_or` + /// - `and_then` to `and` + /// - `or_else` to `or` + /// - `get_or_insert_with` to `get_or_insert` + /// - `ok_or_else` to `ok_or` /// /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. /// - /// **Known problems:** None. + /// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have + /// side effects. Eagerly evaluating them can change the semantics of the program. /// /// **Example:** /// From a7cc5d40683b9351c35a627b05886f43fdec684f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 15 Jun 2020 15:26:02 +0200 Subject: [PATCH 0452/1110] Also simplify if the closure body is an index expression --- clippy_lints/src/methods/mod.rs | 10 ++++ tests/ui/unnecessary_lazy_eval.fixed | 60 ++++++++++++----------- tests/ui/unnecessary_lazy_eval.rs | 60 ++++++++++++----------- tests/ui/unnecessary_lazy_eval.stderr | 68 ++++++++++++++++----------- 4 files changed, 116 insertions(+), 82 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c672ca41dec..ed8eaba75d1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2753,6 +2753,16 @@ fn lint_lazy_eval<'a, 'tcx>( // Closures returning literals can be unconditionally simplified hir::ExprKind::Lit(_) => true, + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if !expr_uses_argument(object, params) { + // arguments are not used as index + !expr_uses_argument(index, params) + } else { + false + } + }, + // Reading fields can be simplified if the object is not an argument of the closure hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index fcfa6dfe12d..c806cf8dce4 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -3,15 +3,15 @@ #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] -struct Deep(Option); +struct Deep(Option); #[derive(Copy, Clone)] struct SomeStruct { - some_field: u32, + some_field: usize, } impl SomeStruct { - fn return_some_field(&self) -> u32 { + fn return_some_field(&self) -> usize { self.some_field } } @@ -22,6 +22,7 @@ fn some_call() -> T { fn main() { let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; // Should lint - Option @@ -30,19 +31,21 @@ fn main() { let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.unwrap_or(ext_arr[0]); let _ = opt.and(ext_opt); let _ = opt.or(ext_opt); let _ = opt.or(None); let _ = opt.get_or_insert(2); let _ = opt.ok_or(2); + let _ = opt.ok_or(ext_arr[0]); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or(2); let _ = Some(10).and(ext_opt); - let _: Option = None.or(ext_opt); + let _: Option = None.or(ext_opt); let _ = None.get_or_insert(2); - let _: Result = None.ok_or(2); - let _: Option = None.or(None); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); let mut deep = Deep(Some(42)); let _ = deep.0.unwrap_or(2); @@ -55,20 +58,22 @@ fn main() { let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); - let _: Result = opt.ok_or_else(|| some_call()); - let _: Result = opt.ok_or_else(some_call); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); // These are handled by bind_instead_of_map - let _: Option = None.or_else(|| Some(3)); + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); // Should lint - Result - let res: Result = Err(5); - let res2: Result = Err(SomeStruct { some_field: 5 }); + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); let _ = res2.unwrap_or(2); let _ = res2.unwrap_or(astronomers_pi); @@ -76,30 +81,31 @@ fn main() { // Should not lint - Result let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); let _ = res2.unwrap_or_else(|err| err.some_field); let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); - let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.or_else(|err| Err(err)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); // These are handled by bind_instead_of_map - let _: Result = res.and_then(|_| Ok(2)); - let _: Result = res.and_then(|_| Ok(astronomers_pi)); - let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 04b3c8ae1e2..dfc6d3ba573 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -3,15 +3,15 @@ #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] -struct Deep(Option); +struct Deep(Option); #[derive(Copy, Clone)] struct SomeStruct { - some_field: u32, + some_field: usize, } impl SomeStruct { - fn return_some_field(&self) -> u32 { + fn return_some_field(&self) -> usize { self.some_field } } @@ -22,6 +22,7 @@ fn some_call() -> T { fn main() { let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; // Should lint - Option @@ -30,19 +31,21 @@ fn main() { let _ = opt.unwrap_or_else(|| 2); let _ = opt.unwrap_or_else(|| astronomers_pi); let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.unwrap_or_else(|| ext_arr[0]); let _ = opt.and_then(|_| ext_opt); let _ = opt.or_else(|| ext_opt); let _ = opt.or_else(|| None); let _ = opt.get_or_insert_with(|| 2); let _ = opt.ok_or_else(|| 2); + let _ = opt.ok_or_else(|| ext_arr[0]); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or_else(|| 2); let _ = Some(10).and_then(|_| ext_opt); - let _: Option = None.or_else(|| ext_opt); + let _: Option = None.or_else(|| ext_opt); let _ = None.get_or_insert_with(|| 2); - let _: Result = None.ok_or_else(|| 2); - let _: Option = None.or_else(|| None); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); let mut deep = Deep(Some(42)); let _ = deep.0.unwrap_or_else(|| 2); @@ -55,20 +58,22 @@ fn main() { let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); - let _: Result = opt.ok_or_else(|| some_call()); - let _: Result = opt.ok_or_else(some_call); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); // These are handled by bind_instead_of_map - let _: Option = None.or_else(|| Some(3)); + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); // Should lint - Result - let res: Result = Err(5); - let res2: Result = Err(SomeStruct { some_field: 5 }); + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); let _ = res2.unwrap_or_else(|_| 2); let _ = res2.unwrap_or_else(|_| astronomers_pi); @@ -76,30 +81,31 @@ fn main() { // Should not lint - Result let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); let _ = res2.unwrap_or_else(|err| err.some_field); let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); - let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.or_else(|err| Err(err)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); // These are handled by bind_instead_of_map - let _: Result = res.and_then(|_| Ok(2)); - let _: Result = res.and_then(|_| Ok(astronomers_pi)); - let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index b941bf84246..15591817540 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:30:13 + --> $DIR/unnecessary_lazy_eval.rs:31:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,43 +7,49 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:31:13 + --> $DIR/unnecessary_lazy_eval.rs:32:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:32:13 + --> $DIR/unnecessary_lazy_eval.rs:33:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:33:13 + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:38:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` @@ -51,83 +57,89 @@ LL | let _ = opt.ok_or_else(|| 2); error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | +LL | let _ = opt.ok_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:26 + --> $DIR/unnecessary_lazy_eval.rs:45:28 | -LL | let _: Option = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:46:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:31 + --> $DIR/unnecessary_lazy_eval.rs:47:35 | -LL | let _: Result = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:26 + --> $DIR/unnecessary_lazy_eval.rs:48:28 | -LL | let _: Option = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:13 + --> $DIR/unnecessary_lazy_eval.rs:51:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:52:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:53:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:74:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:75:13 + --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` @@ -140,5 +152,5 @@ LL | #![warn(clippy::unnecessary_lazy_eval)] | = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors From d7220dbd9143422a5b3a1ebf4dd46b708f83f1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 15 Jun 2020 15:22:22 +0200 Subject: [PATCH 0453/1110] Run cargo dev update_lints --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 4 ++-- src/lintlist/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54489751014..675dfd420a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1754,7 +1754,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold -[`unnecessary_lazy_eval`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_eval +[`unnecessary_lazy_evaluation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluation [`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 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cb29f71387b..ae7045884f7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -672,7 +672,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, - &methods::UNNECESSARY_LAZY_EVALUATION, &methods::OR_FUN_CALL, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, @@ -686,6 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNNECESSARY_LAZY_EVALUATION, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -1542,6 +1542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), @@ -1612,7 +1613,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::OPTION_AS_REF_DEREF), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SUSPICIOUS_MAP), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1e76163f946..2a66d3495ed 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2384,7 +2384,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "unnecessary_lazy_eval", + name: "unnecessary_lazy_evaluation", group: "style", desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", deprecation: None, From 75637c1edac6db37b3c8aa17ef6b5a91db699a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 15 Jun 2020 20:01:18 +0200 Subject: [PATCH 0454/1110] Catch function calls in argument lists, add tests that tuples don't get linted --- clippy_lints/src/methods/mod.rs | 25 ++++++++------ tests/ui/unnecessary_lazy_eval.fixed | 8 ++++- tests/ui/unnecessary_lazy_eval.rs | 8 ++++- tests/ui/unnecessary_lazy_eval.stderr | 48 +++++++++++++-------------- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ed8eaba75d1..463ef48f62c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2744,12 +2744,8 @@ fn lint_lazy_eval<'a, 'tcx>( paths.iter().any(|candidate| match_qpath(path, candidate)) } - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; - - let simplify = match ex.kind { + fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { // Closures returning literals can be unconditionally simplified hir::ExprKind::Lit(_) => true, @@ -2767,15 +2763,16 @@ fn lint_lazy_eval<'a, 'tcx>( hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(ex, params), + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), // Calls to Some, Ok, Err can be considered literals if they don't derive an argument hir::ExprKind::Call(ref func, ref args) => if_chain! { - if allow_variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map if let hir::ExprKind::Path(ref path) = func.kind; if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); then { - !args.iter().any(|arg| expr_uses_argument(arg, params)) + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) } else { false } @@ -2784,9 +2781,15 @@ fn lint_lazy_eval<'a, 'tcx>( // For anything more complex than the above, a closure is probably the right solution, // or the case is handled by an other lint _ => false, - }; + } + } - if simplify { + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + if can_simplify(ex, params, allow_variant_calls) { let msg = if is_option { "unnecessary closure used to substitute value for `Option::None`" } else { diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index c806cf8dce4..7f9d90a8569 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -25,9 +25,12 @@ fn main() { let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; - // Should lint - Option let mut opt = Some(42); let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); @@ -56,6 +59,9 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); let _: Result = opt.ok_or_else(|| some_call()); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index dfc6d3ba573..ca8238d6dcf 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -25,9 +25,12 @@ fn main() { let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; - // Should lint - Option let mut opt = Some(42); let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option let _ = opt.unwrap_or_else(|| 2); let _ = opt.unwrap_or_else(|| astronomers_pi); let _ = opt.unwrap_or_else(|| ext_str.some_field); @@ -56,6 +59,9 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); let _: Result = opt.ok_or_else(|| some_call()); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 15591817540..b8ec654e5c7 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:31:13 + --> $DIR/unnecessary_lazy_eval.rs:34:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,139 +7,139 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:32:13 + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:33:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:38:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:39:13 + --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| ext_arr[0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:46:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:13 + --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:28 + --> $DIR/unnecessary_lazy_eval.rs:48:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:49:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:47:35 + --> $DIR/unnecessary_lazy_eval.rs:50:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:28 + --> $DIR/unnecessary_lazy_eval.rs:51:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:78:13 + --> $DIR/unnecessary_lazy_eval.rs:84:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:79:13 + --> $DIR/unnecessary_lazy_eval.rs:85:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:80:13 + --> $DIR/unnecessary_lazy_eval.rs:86:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` From a7083eea1c8821ff187218f55a1ac17b0f2c0fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 26 Jul 2020 20:18:12 +0200 Subject: [PATCH 0455/1110] Removed the extra lifetime parameter --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 463ef48f62c..6693c358a02 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2710,8 +2710,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` -fn lint_lazy_eval<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn lint_lazy_eval<'tcx>( + cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], allow_variant_calls: bool, From 94cf90e5a56bbd9adbf6d6181d046ede9b96a7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 26 Jul 2020 20:52:00 +0200 Subject: [PATCH 0456/1110] Apply suggested change Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6693c358a02..4578c228107 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2717,8 +2717,8 @@ fn lint_lazy_eval<'tcx>( allow_variant_calls: bool, simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(result_type)); if !is_option && !is_result { return; From 9c41822d34c69dbfded4d9d8b4b8962564be80f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 26 Jul 2020 21:08:07 +0200 Subject: [PATCH 0457/1110] Apply suggested change Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4578c228107..70a6b1f5021 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2717,8 +2717,8 @@ fn lint_lazy_eval<'tcx>( allow_variant_calls: bool, simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); if !is_option && !is_result { return; From d8f0a14da1b79f409bb9fb4493dc001986a72ae0 Mon Sep 17 00:00:00 2001 From: Hactar <6060305+HactarCE@users.noreply.github.com> Date: Sun, 16 Aug 2020 14:43:34 -0400 Subject: [PATCH 0458/1110] Fix typo in description of unnecessary_mut_passed --- clippy_lints/src/mut_reference.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index be3ae7ab380..c506440ed79 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -9,8 +9,8 @@ declare_clippy_lint! { /// **What it does:** Detects passing a mutable reference to a function that only /// requires an immutable reference. /// - /// **Why is this bad?** The immutable reference rules out all other references - /// to the value. Also the code misleads about the intent of the call site. + /// **Why is this bad?** The mutable reference rules out all other references to + /// the value. Also the code misleads about the intent of the call site. /// /// **Known problems:** None. /// From d71b418ac511ee604af9320e401a7ff3fd115482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 16 Aug 2020 20:47:50 +0200 Subject: [PATCH 0459/1110] Moved to submodule, don't trigger if map_unwrap_or does --- clippy_lints/src/methods/mod.rs | 136 +++--------------- .../src/methods/unnecessary_lazy_eval.rs | 113 +++++++++++++++ 2 files changed, 132 insertions(+), 117 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_lazy_eval.rs diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70a6b1f5021..bfc89a74742 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; mod unnecessary_filter_map; +mod unnecessary_lazy_eval; use std::borrow::Cow; use std::fmt; @@ -1436,17 +1437,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { - lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"); - lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]); + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + } }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { - lint_lazy_eval(cx, expr, arg_lists[0], false, "and"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { - lint_lazy_eval(cx, expr, arg_lists[0], false, "or"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1490,9 +1492,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), - ["unwrap_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"), - ["get_or_insert_with", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "get_or_insert"), - ["ok_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "ok_or"), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -2708,119 +2710,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } -/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be -/// replaced with `(return value of simple closure)` -fn lint_lazy_eval<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - args: &'tcx [hir::Expr<'_>], - allow_variant_calls: bool, - simplify_using: &str, -) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); - - if !is_option && !is_result { - return; - } - - // Return true if the expression is an accessor of any of the arguments - fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { - params.iter().any(|arg| { - if_chain! { - if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; - if let [p, ..] = path.segments; - then { - ident.name == p.ident.name - } else { - false - } - } - }) - } - - fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { - paths.iter().any(|candidate| match_qpath(path, candidate)) - } - - fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { - match expr.kind { - // Closures returning literals can be unconditionally simplified - hir::ExprKind::Lit(_) => true, - - hir::ExprKind::Index(ref object, ref index) => { - // arguments are not being indexed into - if !expr_uses_argument(object, params) { - // arguments are not used as index - !expr_uses_argument(index, params) - } else { - false - } - }, - - // Reading fields can be simplified if the object is not an argument of the closure - hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), - - // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), - - // Calls to Some, Ok, Err can be considered literals if they don't derive an argument - hir::ExprKind::Call(ref func, ref args) => if_chain! { - if variant_calls; // Disable lint when rules conflict with bind_instead_of_map - if let hir::ExprKind::Path(ref path) = func.kind; - if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); - then { - // Recursively check all arguments - args.iter().all(|arg| can_simplify(arg, params, variant_calls)) - } else { - false - } - }, - - // For anything more complex than the above, a closure is probably the right solution, - // or the case is handled by an other lint - _ => false, - } - } - - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; - - if can_simplify(ex, params, allow_variant_calls) { - let msg = if is_option { - "unnecessary closure used to substitute value for `Option::None`" - } else { - "unnecessary closure used to substitute value for `Result::Err`" - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_LAZY_EVALUATION, - expr.span, - msg, - &format!("Use `{}` instead", simplify_using), - format!( - "{0}.{1}({2})", - snippet(cx, args[0].span, ".."), - simplify_using, - snippet(cx, ex.span, ".."), - ), - Applicability::MachineApplicable, - ); - } - } -} - /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], -) { +) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type)); @@ -2832,10 +2728,10 @@ fn lint_map_unwrap_or_else<'tcx>( let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { - return; + return false; } } else { - return; + return false; } // lint message @@ -2865,9 +2761,15 @@ fn lint_map_unwrap_or_else<'tcx>( map_snippet, unwrap_snippet, ), ); + true } else if same_span && multiline { span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - }; + true + } else { + false + } + } else { + false } } diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..0dbedc4919c --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -0,0 +1,113 @@ +use crate::utils::{match_qpath, span_lint_and_sugg, snippet, is_type_diagnostic_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::UNNECESSARY_LAZY_EVALUATION; + +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +pub(super) fn lint<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); + + if !is_option && !is_result { + return; + } + + // Return true if the expression is an accessor of any of the arguments + fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) + } + + fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) + } + + fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if !expr_uses_argument(object, params) { + // arguments are not used as index + !expr_uses_argument(index, params) + } else { + false + } + }, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + } + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + if can_simplify(ex, params, allow_variant_calls) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATION, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } +} From 8a14c115369f163e7f96544df48b26376a06a3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 16 Aug 2020 20:50:30 +0200 Subject: [PATCH 0460/1110] Cleanup, explain return value --- clippy_lints/src/methods/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bfc89a74742..c2b2cb98012 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2711,6 +2711,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -2761,16 +2762,14 @@ fn lint_map_unwrap_or_else<'tcx>( map_snippet, unwrap_snippet, ), ); - true + return true; } else if same_span && multiline { span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - true - } else { - false + return true; } - } else { - false } + + false } /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s From 3b52d7f780f2023d6596fe73c0a71f663b26bc33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 16 Aug 2020 20:51:16 +0200 Subject: [PATCH 0461/1110] run cargo dev fmt --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 0dbedc4919c..b44089f7bfc 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, span_lint_and_sugg, snippet, is_type_diagnostic_item}; +use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; From b7ee8685ac83e0f3f32ac5ab5d597d5451d07057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 16 Aug 2020 21:04:02 +0200 Subject: [PATCH 0462/1110] Fix dogfooding test errors --- .../src/methods/unnecessary_lazy_eval.rs | 172 +++++++++--------- 1 file changed, 85 insertions(+), 87 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index b44089f7bfc..a3e7e9971f8 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -6,6 +6,66 @@ use rustc_lint::LateContext; use super::UNNECESSARY_LAZY_EVALUATION; +// Return true if the expression is an accessor of any of the arguments +fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) +} + +fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) +} + +fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if expr_uses_argument(object, params) { + false + } else { + // arguments are not used as index + !expr_uses_argument(index, params) + } + }, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + } +} + /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` pub(super) fn lint<'tcx>( @@ -18,96 +78,34 @@ pub(super) fn lint<'tcx>( let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); - if !is_option && !is_result { - return; - } + if is_option || is_result { + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; - // Return true if the expression is an accessor of any of the arguments - fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { - params.iter().any(|arg| { - if_chain! { - if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; - if let [p, ..] = path.segments; - then { - ident.name == p.ident.name + if can_simplify(ex, params, allow_variant_calls) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" } else { - false - } + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATION, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); } - }) - } - - fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { - paths.iter().any(|candidate| match_qpath(path, candidate)) - } - - fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { - match expr.kind { - // Closures returning literals can be unconditionally simplified - hir::ExprKind::Lit(_) => true, - - hir::ExprKind::Index(ref object, ref index) => { - // arguments are not being indexed into - if !expr_uses_argument(object, params) { - // arguments are not used as index - !expr_uses_argument(index, params) - } else { - false - } - }, - - // Reading fields can be simplified if the object is not an argument of the closure - hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), - - // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), - - // Calls to Some, Ok, Err can be considered literals if they don't derive an argument - hir::ExprKind::Call(ref func, ref args) => if_chain! { - if variant_calls; // Disable lint when rules conflict with bind_instead_of_map - if let hir::ExprKind::Path(ref path) = func.kind; - if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); - then { - // Recursively check all arguments - args.iter().all(|arg| can_simplify(arg, params, variant_calls)) - } else { - false - } - }, - - // For anything more complex than the above, a closure is probably the right solution, - // or the case is handled by an other lint - _ => false, - } - } - - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; - - if can_simplify(ex, params, allow_variant_calls) { - let msg = if is_option { - "unnecessary closure used to substitute value for `Option::None`" - } else { - "unnecessary closure used to substitute value for `Result::Err`" - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_LAZY_EVALUATION, - expr.span, - msg, - &format!("Use `{}` instead", simplify_using), - format!( - "{0}.{1}({2})", - snippet(cx, args[0].span, ".."), - simplify_using, - snippet(cx, ex.span, ".."), - ), - Applicability::MachineApplicable, - ); } } } From b175642a85f5d1507b35b8ef269ecbdaef9aa27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 16 Aug 2020 21:33:29 +0200 Subject: [PATCH 0463/1110] Fix missed rename --- tests/ui/unnecessary_lazy_eval.rs | 2 +- tests/ui/unnecessary_lazy_eval.stderr | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index ca8238d6dcf..fd8f8ed0329 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_eval)] +#![warn(clippy::unnecessary_lazy_evaluation)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index b8ec654e5c7..e86b7ed6253 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -144,13 +144,5 @@ error: unnecessary closure used to substitute value for `Result::Err` LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` -error: unknown clippy lint: clippy::unnecessary_lazy_eval - --> $DIR/unnecessary_lazy_eval.rs:2:9 - | -LL | #![warn(clippy::unnecessary_lazy_eval)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_lazy_evaluation` - | - = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` - -error: aborting due to 25 previous errors +error: aborting due to 24 previous errors From fc1e07e0c1803edb3ade2db2f46034cf227642c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 16 Aug 2020 22:16:39 +0200 Subject: [PATCH 0464/1110] Rename lint to use plural form --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +++--- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 4 ++-- src/lintlist/mod.rs | 2 +- tests/ui/unnecessary_lazy_eval.fixed | 2 +- tests/ui/unnecessary_lazy_eval.rs | 2 +- tests/ui/unnecessary_lazy_eval.stderr | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 675dfd420a7..f662de122f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1754,7 +1754,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold -[`unnecessary_lazy_evaluation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluation +[`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 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ae7045884f7..17501e8e6da 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,7 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, - &methods::UNNECESSARY_LAZY_EVALUATION, + &methods::UNNECESSARY_LAZY_EVALUATIONS, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -1361,7 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1542,7 +1542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c2b2cb98012..0f50a4c813a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1361,7 +1361,7 @@ declare_clippy_lint! { /// /// opt.unwrap_or(42); /// ``` - pub UNNECESSARY_LAZY_EVALUATION, + pub UNNECESSARY_LAZY_EVALUATIONS, style, "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } @@ -1415,7 +1415,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, - UNNECESSARY_LAZY_EVALUATION, + UNNECESSARY_LAZY_EVALUATIONS, ]); impl<'tcx> LateLintPass<'tcx> for Methods { diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a3e7e9971f8..31517659c34 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use super::UNNECESSARY_LAZY_EVALUATION; +use super::UNNECESSARY_LAZY_EVALUATIONS; // Return true if the expression is an accessor of any of the arguments fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { @@ -93,7 +93,7 @@ pub(super) fn lint<'tcx>( span_lint_and_sugg( cx, - UNNECESSARY_LAZY_EVALUATION, + UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, &format!("Use `{}` instead", simplify_using), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 2a66d3495ed..3229c8da507 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2384,7 +2384,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "unnecessary_lazy_evaluation", + name: "unnecessary_lazy_evaluations", group: "style", desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", deprecation: None, diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 7f9d90a8569..fa66e68794e 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_evaluation)] +#![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index fd8f8ed0329..04f47d1aa29 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_evaluation)] +#![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index e86b7ed6253..5c1b2eb1f14 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -4,7 +4,7 @@ error: unnecessary closure used to substitute value for `Option::None` LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` | - = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 From 6afa4ef60f973218c901d0f802d586fe6c43017d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: [PATCH 0465/1110] Introduce function for comparing expression values Introduce `eq_expr_value(cx, a, b)` as a shortcut for `SpanlessEq::new(cx).ignore_fn().eq_expr(cx, a, b)`. No functional changes intended. --- clippy_lints/src/assign_ops.rs | 14 +++++------ clippy_lints/src/booleans.rs | 10 ++++---- clippy_lints/src/copies.rs | 7 +++--- clippy_lints/src/double_comparison.rs | 5 ++-- clippy_lints/src/eq_op.rs | 4 ++-- clippy_lints/src/floating_point_arithmetic.rs | 24 ++++++++----------- clippy_lints/src/question_mark.rs | 6 ++--- clippy_lints/src/swap.rs | 14 +++++------ clippy_lints/src/utils/hir_utils.rs | 5 ++++ clippy_lints/src/utils/internal_lints.rs | 3 +-- clippy_lints/src/utils/mod.rs | 2 +- 11 files changed, 45 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index dab1e96e282..b3185b88840 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,5 +1,5 @@ use crate::utils::{ - get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, }; use crate::utils::{higher, sugg}; use if_chain::if_chain; @@ -70,11 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { return; } // lhs op= l op r - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) { + if eq_expr_value(cx, lhs, l) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r); } // lhs op= l commutative_op r - if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) { + if is_commutative(op.node) && eq_expr_value(cx, lhs, r) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l); } } @@ -161,14 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { if visitor.counter == 1 { // a = a op b - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) { + if eq_expr_value(cx, assignee, l) { lint(assignee, r); } // a = b commutative_op a // Limited to primitive type as these ops are know to be commutative - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) - && cx.typeck_results().expr_ty(assignee).is_primitive_ty() - { + if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() { match op.node { hir::BinOpKind::Add | hir::BinOpKind::Mul @@ -253,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) { + if eq_expr_value(self.cx, self.assignee, expr) { self.counter += 1; } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 18529f2113e..280a2c7fe67 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,6 +1,6 @@ use crate::utils::{ - get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg, - span_lint_and_then, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, + span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -128,7 +128,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } } for (n, expr) in self.terminals.iter().enumerate() { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) { + if eq_expr_value(self.cx, e, expr) { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -138,8 +138,8 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if implements_ord(self.cx, e_lhs); if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; if negate(e_binop.node) == Some(expr_binop.node); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs); + if eq_expr_value(self.cx, e_lhs, expr_lhs); + if eq_expr_value(self.cx, e_rhs, expr_rhs); then { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1f8bff8d71e..10a64769585 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ +use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use crate::utils::{SpanlessEq, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -197,8 +197,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { h.finish() }; - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) }; + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) }; for (i, j) in search_same(conds, hash, eq) { span_lint_and_note( @@ -222,7 +221,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not spawn warning if `IFS_SAME_COND` already produced it. - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) { + if eq_expr_value(cx, lhs, rhs) { return false; } SpanlessEq::new(cx).eq_expr(lhs, rhs) diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index bae7c4647d4..19f56195ec1 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. @@ -46,8 +46,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - let mut spanless_eq = SpanlessEq::new(cx).ignore_fn(); - if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) { + if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { return; } macro_rules! lint_double_comparison { diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 140cd21c34e..e16ec783fab 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq, + eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) { + if is_valid_operator(op) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 93f6ec92ec7..1b02cee126d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,7 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -363,8 +363,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; - if are_exprs_equal(cx, lmul_lhs, lmul_rhs); - if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + if eq_expr_value(cx, lmul_lhs, lmul_rhs); + if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); } @@ -502,8 +502,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -515,8 +515,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -524,10 +524,6 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } } -fn are_exprs_equal(cx: &LateContext<'_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool { - SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2) -} - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match constant_simple(cx, cx.typeck_results(), expr) { @@ -546,12 +542,12 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// returns None. fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind { - if are_exprs_equal(cx, expr1_negated, expr2) { + if eq_expr_value(cx, expr1_negated, expr2) { return Some((false, expr2)); } } if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind { - if are_exprs_equal(cx, expr1, expr2_negated) { + if eq_expr_value(cx, expr1, expr2_negated) { return Some((true, expr1)); } } @@ -614,7 +610,7 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_> args_a.len() == args_b.len() && ( ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || - method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1]) ); } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index fb12c565afd..dbc676ae224 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -7,8 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::sugg::Sugg; use crate::utils::{ - higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, - span_lint_and_sugg, SpanlessEq, + eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, + span_lint_and_sugg, }; declare_clippy_lint! { @@ -65,7 +65,7 @@ impl QuestionMark { if let ExprKind::Block(block, None) = &else_.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; - if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr); + if eq_expr_value(cx, subject, block_expr); then { replacement = Some(format!("Some({}?)", receiver_str)); } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 754f87e6b55..cc39f060fc7 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty, - SpanlessEq, + differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, + walk_ptrs_ty, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -92,8 +92,8 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if rhs2.segments.len() == 1; if ident.as_str() == rhs2.segments[0].ident.as_str(); - if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2); + if eq_expr_value(cx, tmp_init, lhs1); + if eq_expr_value(cx, rhs1, lhs2); then { if let ExprKind::Field(ref lhs1, _) = lhs1.kind { if let ExprKind::Field(ref lhs2, _) = lhs2.kind { @@ -193,7 +193,7 @@ enum Slice<'a> { fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) { + if eq_expr_value(cx, lhs1, lhs2) { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); if matches!(ty.kind, ty::Slice(_)) @@ -221,8 +221,8 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { if !differing_macro_contexts(first.span, second.span); if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0); + if eq_expr_value(cx, lhs0, rhs1); + if eq_expr_value(cx, lhs1, rhs0); then { let lhs0 = Sugg::hir_opt(cx, lhs0); let rhs0 = Sugg::hir_opt(cx, rhs0); diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 28fb6ed12a0..785c409260e 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -340,6 +340,11 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Checks if two expressions evaluate to the same value, and don't contain any side effects. +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { + SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) +} + /// Type used to hash an ast element. This is different from the `Hash` trait /// on ast types as this /// trait would consider IDs and spans. diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 6c235679914..0b8d0bd9e11 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,6 @@ -use crate::utils::SpanlessEq; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, + span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 223628cc610..530552f7940 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -21,7 +21,7 @@ pub mod sugg; pub mod usage; pub use self::attrs::*; pub use self::diagnostics::*; -pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::mem; From 9b800b1e929d6023e1813b2b189336a4bddcffd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: [PATCH 0466/1110] Rename SpanlessEq::ignore_fn to deny_side_effects No functional changes intended. --- clippy_lints/src/utils/hir_utils.rs | 19 +++++++++---------- clippy_lints/src/utils/internal_lints.rs | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 785c409260e..1014546ff89 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -23,9 +23,7 @@ pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, - /// If is true, never consider as equal expressions containing function - /// calls. - ignore_fn: bool, + allow_side_effects: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -33,13 +31,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - ignore_fn: false, + allow_side_effects: true, } } - pub fn ignore_fn(self) -> Self { + /// Consider expressions containing potential side effects as not equal. + pub fn deny_side_effects(self) -> Self { Self { - ignore_fn: true, + allow_side_effects: false, ..self } } @@ -67,7 +66,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { #[allow(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if self.ignore_fn && differing_macro_contexts(left.span, right.span) { + if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } @@ -108,7 +107,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { - !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { @@ -134,7 +133,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { - !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); @@ -342,7 +341,7 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - /// Checks if two expressions evaluate to the same value, and don't contain any side effects. pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { - SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) + SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) } /// Type used to hash an ast element. This is different from the `Hash` trait diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0b8d0bd9e11..8fa5d22210a 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -492,7 +492,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if let StmtKind::Semi(only_expr) = &stmts[0].kind; if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).ignore_fn(); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); then { match &*ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { From d1dbf7913ad83954337e94b094f22f1d9d8b83f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: [PATCH 0467/1110] Expresions with Assign / AssignOp have side effects --- clippy_lints/src/utils/hir_utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 1014546ff89..cacc9f8d6f2 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -89,10 +89,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { - self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { - lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { From 4f4abf4e0640edbb1614f3dcb8ff62e8afc54801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: [PATCH 0468/1110] Warn about explicit self-assignment Warn about assignments where left-hand side place expression is the same as right-hand side value expression. For example, warn about assignment in: ```rust pub struct Event { id: usize, x: i32, y: i32, } pub fn copy_position(a: &mut Event, b: &Event) { a.x = b.x; a.y = a.y; } ``` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/self_assignment.rs | 51 +++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/self_assignment.rs | 67 +++++++++++++++++++++++++++ tests/ui/self_assignment.stderr | 70 +++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+) create mode 100644 clippy_lints/src/self_assignment.rs create mode 100644 tests/ui/self_assignment.rs create mode 100644 tests/ui/self_assignment.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f662de122f9..5ce63c0a157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1690,6 +1690,7 @@ Released 2018-09-13 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 17501e8e6da..87c297e72eb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -284,6 +284,7 @@ mod reference; mod regex; mod repeat_once; mod returns; +mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; @@ -773,6 +774,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &repeat_once::REPEAT_ONCE, &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, + &self_assignment::SELF_ASSIGNMENT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1090,6 +1092,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1421,6 +1424,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1714,6 +1718,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs new file mode 100644 index 00000000000..e096c9aebc1 --- /dev/null +++ b/clippy_lints/src/self_assignment.rs @@ -0,0 +1,51 @@ +use crate::utils::{eq_expr_value, snippet, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit self-assignments. + /// + /// **Why is this bad?** Self-assignments are redundant and unlikely to be + /// intentional. + /// + /// **Known problems:** If expression contains any deref coercions or + /// indexing operations they are assumed not to have any side effects. + /// + /// **Example:** + /// + /// ```rust + /// struct Event { + /// id: usize, + /// x: i32, + /// y: i32, + /// } + /// + /// fn copy_position(a: &mut Event, b: &Event) { + /// a.x = b.x; + /// a.y = a.y; + /// } + /// ``` + pub SELF_ASSIGNMENT, + correctness, + "explicit self-assignment" +} + +declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]); + +impl<'tcx> LateLintPass<'tcx> for SelfAssignment { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind { + if eq_expr_value(cx, lhs, rhs) { + let lhs = snippet(cx, lhs.span, ""); + let rhs = snippet(cx, rhs.span, ""); + span_lint( + cx, + SELF_ASSIGNMENT, + expr.span, + &format!("self-assignment of `{}` to `{}`", rhs, lhs), + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3229c8da507..bf58c117aaa 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1956,6 +1956,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "self_assignment", + group: "correctness", + desc: "explicit self-assignment", + deprecation: None, + module: "self_assignment", + }, Lint { name: "serde_api_misuse", group: "correctness", diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs new file mode 100644 index 00000000000..a7cbb9cd78b --- /dev/null +++ b/tests/ui/self_assignment.rs @@ -0,0 +1,67 @@ +#![warn(clippy::self_assignment)] + +pub struct S<'a> { + a: i32, + b: [i32; 10], + c: Vec>, + e: &'a mut i32, + f: &'a mut i32, +} + +pub fn positives(mut a: usize, b: &mut u32, mut s: S) { + a = a; + *b = *b; + s = s; + s.a = s.a; + s.b[10] = s.b[5 + 5]; + s.c[0][1] = s.c[0][1]; + s.b[a] = s.b[a]; + *s.e = *s.e; + s.b[a + 10] = s.b[10 + a]; + + let mut t = (0, 1); + t.1 = t.1; + t.0 = (t.0); +} + +pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { + dbg!(&a); + a = *b; + dbg!(&a); + s.b[1] += s.b[1]; + s.b[1] = s.b[2]; + s.c[1][0] = s.c[0][1]; + s.b[a] = s.b[*b]; + s.b[a + 10] = s.b[a + 11]; + *s.e = *s.f; + + let mut t = (0, 1); + t.0 = t.1; +} + +#[allow(clippy::eval_order_dependence)] +pub fn negatives_side_effects() { + let mut v = vec![1, 2, 3, 4, 5]; + let mut i = 0; + v[{ + i += 1; + i + }] = v[{ + i += 1; + i + }]; + + fn next(n: &mut usize) -> usize { + let v = *n; + *n += 1; + v + } + + let mut w = vec![1, 2, 3, 4, 5]; + let mut i = 0; + let i = &mut i; + w[next(i)] = w[next(i)]; + w[next(i)] = w[next(i)]; +} + +fn main() {} diff --git a/tests/ui/self_assignment.stderr b/tests/ui/self_assignment.stderr new file mode 100644 index 00000000000..826e0d0ba88 --- /dev/null +++ b/tests/ui/self_assignment.stderr @@ -0,0 +1,70 @@ +error: self-assignment of `a` to `a` + --> $DIR/self_assignment.rs:12:5 + | +LL | a = a; + | ^^^^^ + | + = note: `-D clippy::self-assignment` implied by `-D warnings` + +error: self-assignment of `*b` to `*b` + --> $DIR/self_assignment.rs:13:5 + | +LL | *b = *b; + | ^^^^^^^ + +error: self-assignment of `s` to `s` + --> $DIR/self_assignment.rs:14:5 + | +LL | s = s; + | ^^^^^ + +error: self-assignment of `s.a` to `s.a` + --> $DIR/self_assignment.rs:15:5 + | +LL | s.a = s.a; + | ^^^^^^^^^ + +error: self-assignment of `s.b[5 + 5]` to `s.b[10]` + --> $DIR/self_assignment.rs:16:5 + | +LL | s.b[10] = s.b[5 + 5]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.c[0][1]` to `s.c[0][1]` + --> $DIR/self_assignment.rs:17:5 + | +LL | s.c[0][1] = s.c[0][1]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.b[a]` to `s.b[a]` + --> $DIR/self_assignment.rs:18:5 + | +LL | s.b[a] = s.b[a]; + | ^^^^^^^^^^^^^^^ + +error: self-assignment of `*s.e` to `*s.e` + --> $DIR/self_assignment.rs:19:5 + | +LL | *s.e = *s.e; + | ^^^^^^^^^^^ + +error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]` + --> $DIR/self_assignment.rs:20:5 + | +LL | s.b[a + 10] = s.b[10 + a]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `t.1` to `t.1` + --> $DIR/self_assignment.rs:23:5 + | +LL | t.1 = t.1; + | ^^^^^^^^^ + +error: self-assignment of `(t.0)` to `t.0` + --> $DIR/self_assignment.rs:24:5 + | +LL | t.0 = (t.0); + | ^^^^^^^^^^^ + +error: aborting due to 11 previous errors + From 99ba290a14c6654164cea8339b827d8da0ff600d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 17 Aug 2020 08:36:02 +0900 Subject: [PATCH 0469/1110] Improve code style --- clippy_lints/src/loops.rs | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f7db2563d2b..df06031d999 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1145,29 +1145,24 @@ fn detect_same_item_push<'tcx>( let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id) { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); let node = cx.tcx.hir().get(hir_id); - if_chain! { - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - then { - match bind_ann { - BindingAnnotation::RefMut | BindingAnnotation::Mutable => {}, - _ => { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } - } - } + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) } } } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { From 262db3b5e6c3f3278c9fb2a91817f3a66cf0a5e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 17 Aug 2020 12:19:32 +0200 Subject: [PATCH 0470/1110] deps: bump cargo_metadata and semver cargo_metadata 0.9.1 -> 0.11.1 semver 0.9.0 -> 0.10.0 --- Cargo.toml | 4 ++-- clippy_lints/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 836897927b0..c7a3099b8ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,13 +31,13 @@ path = "src/driver.rs" # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update -semver = "0.9" +semver = "0.10" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" [dev-dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11.1" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" lazy_static = "1.0" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index e959c1a6511..cc7d3a04f00 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2018" [dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" @@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" -semver = "0.9.0" +semver = "0.10.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } From df4d42fc2debf7a7d43226c79480c71037be42b8 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 18 Aug 2020 01:00:11 +0200 Subject: [PATCH 0471/1110] transmute: avoid suggesting from/to bits in const --- clippy_lints/src/transmute.rs | 10 +++-- tests/ui/transmute.rs | 15 ++++++-- tests/ui/transmute.stderr | 52 +++++++++++++------------- tests/ui/transmute_float_to_int.rs | 11 +++++- tests/ui/transmute_float_to_int.stderr | 12 +++--- 5 files changed, 61 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 28fd55f6ff0..1d238b242b0 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, + in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, span_lint_and_then, sugg, }; use if_chain::if_chain; @@ -331,6 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { + // Avoid suggesting f32::(from|to)_bits in const contexts. + // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + let const_context = in_constant(cx, e.hir_id); + let from_ty = cx.typeck_results().expr_ty(&args[0]); let to_ty = cx.typeck_results().expr_ty(e); @@ -544,7 +548,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, ) }, - (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then( + (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_INT_TO_FLOAT, e.span, @@ -567,7 +571,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); }, ), - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then( + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_FLOAT_TO_INT, e.span, diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index bb853d23704..7caad34edb3 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,3 +1,4 @@ +#![feature(const_fn_transmute)] #![allow(dead_code)] extern crate core; @@ -81,9 +82,17 @@ fn int_to_bool() { } #[warn(clippy::transmute_int_to_float)] -fn int_to_float() { - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - let _: f32 = unsafe { std::mem::transmute(0_i32) }; +mod int_to_float { + fn test() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; + } + + // See issue #5747 + const VALUE: f32 = unsafe { std::mem::transmute(0_u32) }; + const fn from_bits(v: u32) -> f32 { + unsafe { std::mem::transmute(v) } + } } fn bytes_to_str(b: &[u8], mb: &mut [u8]) { diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 8582080498f..d817c08b52f 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 + --> $DIR/transmute.rs:20:20 | LL | let _: &'a T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,67 +7,67 @@ LL | let _: &'a T = core::intrinsics::transmute(t); = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:63:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +75,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:65:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:67:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:69:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:75:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +101,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:76:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:81:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,21 +115,21 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:85:27 + --> $DIR/transmute.rs:87:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` | = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:86:27 + --> $DIR/transmute.rs:88:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:90:28 + --> $DIR/transmute.rs:99:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,7 +137,7 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:91:32 + --> $DIR/transmute.rs:100:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index ce942751ada..8173944d959 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -1,4 +1,5 @@ -#[warn(clippy::transmute_float_to_int)] +#![feature(const_fn_transmute)] +#![warn(clippy::transmute_float_to_int)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -9,4 +10,12 @@ fn float_to_int() { let _: u64 = unsafe { std::mem::transmute(-1.0) }; } +mod issue_5747 { + const VALUE: u32 = unsafe { std::mem::transmute(1f32) }; + + const fn to_bits(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } +} + fn main() {} diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr index eb786bb39f9..5a40cf381d6 100644 --- a/tests/ui/transmute_float_to_int.stderr +++ b/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> $DIR/transmute_float_to_int.rs:4:27 + --> $DIR/transmute_float_to_int.rs:5:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -7,31 +7,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` error: transmute from a `f32` to a `i32` - --> $DIR/transmute_float_to_int.rs:5:27 + --> $DIR/transmute_float_to_int.rs:6:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:6:27 + --> $DIR/transmute_float_to_int.rs:7:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> $DIR/transmute_float_to_int.rs:7:27 + --> $DIR/transmute_float_to_int.rs:8:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:8:27 + --> $DIR/transmute_float_to_int.rs:9:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:9:27 + --> $DIR/transmute_float_to_int.rs:10:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` From 6a12bae1941bdef7c2716ffefed5594e5d7e9f8e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 18 Aug 2020 22:19:30 +0200 Subject: [PATCH 0472/1110] no from/to bits in const: add tests cases for f64 --- clippy_lints/src/transmute.rs | 2 +- tests/ui/transmute.rs | 17 +++++++++++++---- tests/ui/transmute.stderr | 18 +++++++++++++++--- tests/ui/transmute_float_to_int.rs | 9 +++++++-- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 1d238b242b0..50d9c93f9d4 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting f32::(from|to)_bits in const contexts. + // Avoid suggesting from/to bits in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. let const_context = in_constant(cx, e.hir_id); diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 7caad34edb3..9f1948359e7 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -86,12 +86,21 @@ mod int_to_float { fn test() { let _: f32 = unsafe { std::mem::transmute(0_u32) }; let _: f32 = unsafe { std::mem::transmute(0_i32) }; + let _: f64 = unsafe { std::mem::transmute(0_u64) }; + let _: f64 = unsafe { std::mem::transmute(0_i64) }; } - // See issue #5747 - const VALUE: f32 = unsafe { std::mem::transmute(0_u32) }; - const fn from_bits(v: u32) -> f32 { - unsafe { std::mem::transmute(v) } + mod issue_5747 { + const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + + const fn from_bits_32(v: i32) -> f32 { + unsafe { std::mem::transmute(v) } + } + + const fn from_bits_64(v: u64) -> f64 { + unsafe { std::mem::transmute(v) } + } } } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index d817c08b52f..ad9953d12bc 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -128,8 +128,20 @@ error: transmute from a `i32` to a `f32` LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +error: transmute from a `u64` to a `f64` + --> $DIR/transmute.rs:89:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` + +error: transmute from a `i64` to a `f64` + --> $DIR/transmute.rs:90:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` + error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:99:28 + --> $DIR/transmute.rs:108:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,10 +149,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:100:32 + --> $DIR/transmute.rs:109:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index 8173944d959..1040fee4b34 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -11,9 +11,14 @@ fn float_to_int() { } mod issue_5747 { - const VALUE: u32 = unsafe { std::mem::transmute(1f32) }; + const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - const fn to_bits(v: f32) -> u32 { + const fn to_bits_32(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } + + const fn to_bits_64(v: f64) -> i64 { unsafe { std::mem::transmute(v) } } } From 902b28275ef6e06826f269bdf2459c4ba4562145 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 19 Aug 2020 22:31:34 +0900 Subject: [PATCH 0473/1110] Improve lint message in `to_string_in_display` --- clippy_lints/src/to_string_in_display.rs | 4 ++-- src/lintlist/mod.rs | 2 +- tests/ui/to_string_in_display.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 11bdd27d9b1..4b6a0a6a0c9 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { /// ``` pub TO_STRING_IN_DISPLAY, correctness, - "to_string method used while implementing Display trait" + "`to_string` method used while implementing `Display` trait" } #[derive(Default)] @@ -80,7 +80,7 @@ impl LateLintPass<'_> for ToStringInDisplay { cx, TO_STRING_IN_DISPLAY, expr.span, - "Using to_string in fmt::Display implementation might lead to infinite recursion", + "using `to_string` in `fmt::Display` implementation might lead to infinite recursion", ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bf58c117aaa..c50c6b900ae 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2183,7 +2183,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "to_string_in_display", group: "correctness", - desc: "to_string method used while implementing Display trait", + desc: "`to_string` method used while implementing `Display` trait", deprecation: None, module: "to_string_in_display", }, diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr index cbc0a41036b..5f26ef413e2 100644 --- a/tests/ui/to_string_in_display.stderr +++ b/tests/ui/to_string_in_display.stderr @@ -1,4 +1,4 @@ -error: Using to_string in fmt::Display implementation might lead to infinite recursion +error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion --> $DIR/to_string_in_display.rs:25:25 | LL | write!(f, "{}", self.to_string()) From c236c0fb5694948353b5fcbe46010cf04d5a7107 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 20 Aug 2020 06:34:48 +0200 Subject: [PATCH 0474/1110] Fix false positive in `PRECEDENCE` lint Extend the lint to handle chains of methods combined with unary negation. Closes #5924 --- clippy_lints/src/precedence.rs | 59 +++++++++++++++++----------------- tests/ui/precedence.fixed | 8 +++++ tests/ui/precedence.rs | 8 +++++ tests/ui/precedence.stderr | 20 +++++++++++- 4 files changed, 65 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 4797771e7bd..c9d18c3cb72 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,4 +1,5 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -102,36 +103,36 @@ impl EarlyLintPass for Precedence { } } - if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind { - if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind { + if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind { + let mut arg = operand; + + let mut all_odd = true; + while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind { let path_segment_str = path_segment.ident.name.as_str(); - if let Some(slf) = args.first() { - if let ExprKind::Lit(ref lit) = slf.kind { - match lit.kind { - LitKind::Int(..) | LitKind::Float(..) => { - if ALLOWED_ODD_FUNCTIONS - .iter() - .any(|odd_function| **odd_function == *path_segment_str) - { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, rhs.span, "..", &mut applicability) - ), - applicability, - ); - }, - _ => (), - } - } + all_odd &= ALLOWED_ODD_FUNCTIONS + .iter() + .any(|odd_function| **odd_function == *path_segment_str); + arg = args.first().expect("A method always has a receiver."); + } + + if_chain! { + if !all_odd; + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 4d284ae1319..163bd044c17 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -(1.0_f64.cos().cos()); + let _ = -(1.0_f64.cos().sin()); + let _ = -(1.0_f64.sin().cos()); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d08e82f349..8c849e3209b 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -1.0_f64.cos().cos(); + let _ = -1.0_f64.cos().sin(); + let _ = -1.0_f64.sin().cos(); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.stderr b/tests/ui/precedence.stderr index a2ed5392bfc..03d585b3975 100644 --- a/tests/ui/precedence.stderr +++ b/tests/ui/precedence.stderr @@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call LL | -1f32.abs(); | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` -error: aborting due to 9 previous errors +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:52:13 + | +LL | let _ = -1.0_f64.cos().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:53:13 + | +LL | let _ = -1.0_f64.cos().sin(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:54:13 + | +LL | let _ = -1.0_f64.sin().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())` + +error: aborting due to 12 previous errors From 2ecc2ac864739cff6aed2609021e2467dedb117a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 21 Aug 2020 00:07:56 +0200 Subject: [PATCH 0475/1110] unit-arg - improve suggestion --- clippy_lints/src/types.rs | 88 +++++++++++--------- tests/ui/unit_arg.rs | 7 +- tests/ui/unit_arg.stderr | 111 ++++++++++++-------------- tests/ui/unit_arg_empty_blocks.stderr | 21 ++--- 4 files changed, 115 insertions(+), 112 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7e9190bef5e..3f5b3a5bcd5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -11,8 +11,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, - TraitItem, TraitItemKind, TyKind, UnOp, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -844,43 +844,54 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp Applicability::MaybeIncorrect, ); or = "or "; + applicability = Applicability::MaybeIncorrect; }); - let sugg = args_to_recover + + let arg_snippets: Vec = args_to_recover + .iter() + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + let arg_snippets_without_empty_blocks: Vec = args_to_recover .iter() .filter(|arg| !is_empty_block(arg)) - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) - ) - }) - .collect::>(); - let mut and = ""; - if !sugg.is_empty() { - let plural = if sugg.len() > 1 { "s" } else { "" }; - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg.join("\n")), - applicability, - ); - and = "...and " + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + if let Some(mut sugg) = snippet_opt(cx, expr.span) { + arg_snippets.iter().for_each(|arg| { + sugg = sugg.replacen(arg, "()", 1); + }); + sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + // expr is not in a block statement or result expression position, wrap in a block + sugg = format!("{{ {} }}", sugg); + } + + if arg_snippets_without_empty_blocks.is_empty() { + db.multipart_suggestion( + &format!("use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + } else { + let plural = arg_snippets_without_empty_blocks.len() > 1; + let empty_or_s = if plural { "s" } else { "" }; + let it_or_them = if plural { "them" } else { "it" }; + db.span_suggestion( + expr.span, + &format!( + "{}move the expression{} in front of the call and replace {} with the unit literal `()`", + or, empty_or_s, it_or_them + ), + sugg, + applicability, + ); + } } - db.multipart_suggestion( - &format!("{}use {}unit literal{} instead", and, singular, plural), - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); }, ); } @@ -2055,6 +2066,7 @@ impl PartialOrd for FullInt { }) } } + impl Ord for FullInt { #[must_use] fn cmp(&self, other: &Self) -> Ordering { diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2992abae775..2e2bd054e42 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,5 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] use std::fmt::Debug; @@ -47,6 +47,11 @@ fn bad() { foo(3); }, ); + // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block + None.or(Some(foo(2))); + // in this case, the suggestion can be inlined, no need for a surrounding block + // foo(()); foo(()) instead of { foo(()); foo(()) } + foo(foo(())) } fn ok() { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 56f6a855dfa..2a0cc1f18e2 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -11,16 +11,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; foo(()); | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:26:5 @@ -28,14 +24,10 @@ error: passing a unit value to a function LL | foo(foo(1)); | ^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); - | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ +LL | foo(1); foo(()); + | ^^^^^^^^^^^^^^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:27:5 @@ -50,17 +42,13 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | foo(1); LL | foo(2); -LL | }; +LL | }; foo(()); | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:32:5 @@ -74,16 +62,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; b.bar(()); | -help: ...and use a unit literal instead - | -LL | b.bar(()); - | ^^ error: passing unit values to a function --> $DIR/unit_arg.rs:35:5 @@ -91,15 +75,10 @@ error: passing unit values to a function LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead - | -LL | taking_multiple_units((), ()); - | ^^ ^^ +LL | foo(0); foo(1); taking_multiple_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg.rs:36:5 @@ -114,18 +93,13 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | { +LL | foo(0); { LL | foo(1); LL | foo(2); -LL | }; +LL | }; taking_multiple_units((), ()); | -help: ...and use unit literals instead - | -LL | taking_multiple_units((), ()); - | ^^ ^^ error: passing unit values to a function --> $DIR/unit_arg.rs:40:5 @@ -147,35 +121,56 @@ help: remove the semicolon from the last statement in the block | LL | foo(3) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; -LL | { -LL | foo(2); +LL | foo(0); +LL | foo(1); +LL | }; { +LL | foo(2); +LL | foo(3); ... -help: ...and use unit literals instead + +error: use of `or` followed by a function call + --> $DIR/unit_arg.rs:51:10 | -LL | (), -LL | (), +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:51:13 + | +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | None.or({ foo(2); Some(()) }); + | ^^^^^^^^^^^^^^^^^^^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:54:5 + | +LL | foo(foo(())) + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | foo(()); foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:82:5 + --> $DIR/unit_arg.rs:87:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); +LL | foo(1); Some(()) | -help: ...and use a unit literal instead - | -LL | Some(()) - | ^^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index bb58483584b..4cbbc8b8cd4 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -22,14 +22,10 @@ error: passing unit values to a function LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); - | -help: ...and use unit literals instead - | -LL | taking_two_units((), ()); - | ^^ ^^ +LL | foo(0); taking_two_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -37,15 +33,10 @@ error: passing unit values to a function LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead - | -LL | taking_three_units((), (), ()); - | ^^ ^^ ^^ +LL | foo(0); foo(1); taking_three_units((), (), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors From 11efd75aeb8637bd5444104a57f1051b86c0d62b Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 21 Aug 2020 07:23:04 +0200 Subject: [PATCH 0476/1110] Fix false negative in `option_as_ref_deref` --- clippy_lints/src/methods/mod.rs | 7 ++++++- tests/ui/option_as_ref_deref.fixed | 3 +++ tests/ui/option_as_ref_deref.rs | 3 +++ tests/ui/option_as_ref_deref.stderr | 8 +++++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2498c48f067..22942d9fb0c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3421,7 +3421,12 @@ fn lint_option_as_ref_deref<'tcx>( ]; let is_deref = match map_args[1].kind { - hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)), + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_args[1].hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }), hir::ExprKind::Closure(_, _, body_id, _, _) => { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 076692e6445..07d7f0b45b0 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -38,4 +38,7 @@ fn main() { let _ = opt.as_deref(); let _ = opt.as_deref_mut(); + + // Issue #5927 + let _ = opt.as_deref(); } diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 3bf5f715f83..6ae059c9425 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -41,4 +41,7 @@ fn main() { let _ = opt.as_ref().map(|x| &**x); let _ = opt.as_mut().map(|x| &mut **x); + + // Issue #5927 + let _ = opt.as_ref().map(std::ops::Deref::deref); } diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index a106582a633..62f28232475 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -100,5 +100,11 @@ error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` -error: aborting due to 16 previous errors +error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:46:13 + | +LL | let _ = opt.as_ref().map(std::ops::Deref::deref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 17 previous errors From 146e352db4eade947f6f358cd38c308e95783d7b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 22 Aug 2020 00:59:42 +0200 Subject: [PATCH 0477/1110] run cargo dev fmt --- clippy_lints/src/unnested_or_patterns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 0dca6a5da0c..7f4f16f8faf 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -340,7 +340,7 @@ fn take_pat(from: &mut Pat) -> Pat { id: DUMMY_NODE_ID, kind: Wild, span: DUMMY_SP, - tokens: None + tokens: None, }; mem::replace(from, dummy) } From 03bc7aed441240190030c9dfdcce6405014178dd Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 21 Aug 2020 08:18:05 +0200 Subject: [PATCH 0478/1110] Add async test case for `wrong_self_convention` lint --- tests/ui/wrong_self_convention.rs | 13 +++++++++++++ tests/ui/wrong_self_convention.stderr | 24 ++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 99652ca4470..f44305d7e48 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -1,3 +1,4 @@ +// edition:2018 #![warn(clippy::wrong_self_convention)] #![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] @@ -75,3 +76,15 @@ mod issue4293 { fn into_t3(self: Arc) {} } } + +// False positive for async (see #4037) +mod issue4037 { + pub struct Foo; + pub struct Bar; + + impl Foo { + pub async fn into_bar(self) -> Bar { + Bar + } + } +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 0d0eb19cd07..ef3ad73ebc7 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:17:17 + --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ @@ -7,67 +7,67 @@ LL | fn from_i32(self) {} = note: `-D clippy::wrong-self-convention` implied by `-D warnings` error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:23:21 + --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:35:15 + --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:37:17 + --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:39:15 + --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:41:15 + --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:43:17 + --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:45:19 + --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:46:21 + --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:47:19 + --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:48:19 + --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:49:21 + --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ From 191b6c798fa40334ec8898d07b4d28fd76743120 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 22 Aug 2020 10:35:18 +0200 Subject: [PATCH 0479/1110] Don't lint if it has always inline attribute --- clippy_lints/src/trivially_copy_pass_by_ref.rs | 9 +++++++-- tests/ui/trivially_copy_pass_by_ref.rs | 18 ++++++++++++++++++ tests/ui/trivially_copy_pass_by_ref.stderr | 14 +++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 7948d99162b..92f42168a1e 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -2,6 +2,7 @@ use std::cmp; use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -155,8 +156,12 @@ impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { return; } for a in attrs { - if a.meta_item_list().is_some() && a.has_name(sym!(proc_macro_derive)) { - return; + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } } } }, diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index 316426f1cf1..e7e0a31febc 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -97,6 +97,24 @@ mod issue3992 { pub fn c(d: &u16) {} } +mod issue5876 { + // Don't lint here as it is always inlined + #[inline(always)] + fn foo_always(x: &i32) { + println!("{}", x); + } + + #[inline(never)] + fn foo_never(x: &i32) { + println!("{}", x); + } + + #[inline] + fn foo(x: &i32) { + println!("{}", x); + } +} + fn main() { let (mut foo, bar) = (Foo(0), Bar([0; 24])); let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index be0914e4a79..ccc3cdb2b74 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -94,5 +94,17 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method2(&self, _color: &Color); | ^^^^^^ help: consider passing by value instead: `Color` -error: aborting due to 15 previous errors +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:108:21 + | +LL | fn foo_never(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:113:15 + | +LL | fn foo(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: aborting due to 17 previous errors From 5b07b9ed61d1979320e9e63219b5d0d6f4b350f7 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Thu, 20 Aug 2020 22:49:39 +0200 Subject: [PATCH 0480/1110] Widen understanding of prelude import Prelude imports are exempt from wildcard import warnings. Until now only imports of the form ``` use ...::prelude::*; ``` were considered. This change makes it so that the segment `prelude` can show up anywhere, for instance: ``` use ...::prelude::v1::*; ``` Fixes #5917 --- clippy_lints/src/wildcard_imports.rs | 7 ++----- tests/ui/auxiliary/wildcard_imports_helper.rs | 6 ++++++ tests/ui/wildcard_imports.rs | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7eb7c2e980..717741129a8 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -195,13 +195,10 @@ impl WildcardImports { } } -// Allow "...prelude::*" imports. +// 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") + segments.iter().filter(|ps| ps.ident.as_str() == "prelude").count() > 0 } // Allow "super::*" imports in tests. diff --git a/tests/ui/auxiliary/wildcard_imports_helper.rs b/tests/ui/auxiliary/wildcard_imports_helper.rs index 414477aedd7..d75cdd625f9 100644 --- a/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -19,3 +19,9 @@ mod extern_exports { A, } } + +pub mod prelude { + pub mod v1 { + pub struct PreludeModAnywhere; + } +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 3ad1a29aeba..1f261159f4a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); From 53508aeb65963c389233c956105202466128a900 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Thu, 20 Aug 2020 22:54:46 +0200 Subject: [PATCH 0481/1110] Run sh tests/ui/update-all-references.sh --- tests/ui/wildcard_imports.fixed | 2 ++ tests/ui/wildcard_imports.stderr | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 67423e6ec1d..287f8935327 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; use wildcard_imports_helper::{ExternA, extern_foo}; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index fab43b738eb..351988f31ea 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:89:13 + --> $DIR/wildcard_imports.rs:91:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:95:75 + --> $DIR/wildcard_imports.rs:97:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:98:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:20 + --> $DIR/wildcard_imports.rs:109:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:30 + --> $DIR/wildcard_imports.rs:109:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:13 + --> $DIR/wildcard_imports.rs:116:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:143:9 + --> $DIR/wildcard_imports.rs:145:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:152:9 + --> $DIR/wildcard_imports.rs:154:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:153:9 + --> $DIR/wildcard_imports.rs:155:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,31 +93,31 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:164:13 + --> $DIR/wildcard_imports.rs:166:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:199:17 + --> $DIR/wildcard_imports.rs:201:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:13 + --> $DIR/wildcard_imports.rs:209:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:216:17 + --> $DIR/wildcard_imports.rs:218:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:225:13 + --> $DIR/wildcard_imports.rs:227:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` From e615a26ae4fe293cda83961470907e78e4b2ee75 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Fri, 21 Aug 2020 14:03:42 +0200 Subject: [PATCH 0482/1110] Use more elegant way to check for prelude string --- clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 717741129a8..5683a71efea 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -198,7 +198,7 @@ impl WildcardImports { // 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().filter(|ps| ps.ident.as_str() == "prelude").count() > 0 + segments.iter().any(|ps| ps.ident.as_str() == "prelude") } // Allow "super::*" imports in tests. From e8d33d73dc3f9d0daf9b3affe65a2431f5a3e13a Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 23 Aug 2020 07:50:59 +0200 Subject: [PATCH 0483/1110] Fix `let_and_return` bad suggestion Add a cast to the suggestion when the return expression has adjustments. These adjustments are lost when the suggestion is applied. This is similar to the problem in issue #4437. Closes #5729 --- clippy_lints/src/returns.rs | 5 ++++- tests/ui/let_and_return.rs | 21 +++++++++++++++++++++ tests/ui/let_and_return.stderr | 16 +++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c5541e64b4..a6e4252a0c8 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -99,7 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for Return { |err| { err.span_label(local.span, "unnecessary `let` binding"); - if let Some(snippet) = snippet_opt(cx, initexpr.span) { + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + snippet.push_str(" as _"); + } err.multipart_suggestion( "return the expression directly", vec![ diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 09614b8c1ad..73e550b3df8 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -135,4 +135,25 @@ mod no_lint_if_stmt_borrows { } } +mod issue_5729 { + use std::sync::Arc; + + trait Foo {} + + trait FooStorage { + fn foo_cloned(&self) -> Arc; + } + + struct FooStorageImpl { + foo: Arc, + } + + impl FooStorage for FooStorageImpl { + fn foo_cloned(&self) -> Arc { + let clone = Arc::clone(&self.foo); + clone + } + } +} + fn main() {} diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index eacf948b392..fe878e5f206 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -27,5 +27,19 @@ LL | LL | 5 | -error: aborting due to 2 previous errors +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:154:13 + | +LL | let clone = Arc::clone(&self.foo); + | ---------------------------------- unnecessary `let` binding +LL | clone + | ^^^^^ + | +help: return the expression directly + | +LL | +LL | Arc::clone(&self.foo) as _ + | + +error: aborting due to 3 previous errors From 6dd65b8e6a548071b19507826c53bf9a7c36b323 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 20 Aug 2020 23:47:04 +0200 Subject: [PATCH 0484/1110] Fix cargo dev new_lint for late lint passes --- .github/workflows/clippy.yml | 7 +++++++ clippy_dev/src/new_lint.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 5fa8009a8b4..99e371631b1 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -92,6 +92,13 @@ jobs: env: OS: ${{ runner.os }} + - name: Test cargo dev new lint + run: | + cargo dev new_lint --name new_early_pass --pass early + cargo dev new_lint --name new_late_pass --pass late + cargo check + git reset --hard HEAD + # Cleanup - name: Run cargo-cache --autoclean run: | diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 1e032a7bc20..d951ca0e630 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -47,7 +47,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str fn create_lint(lint: &LintData) -> io::Result<()> { let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), - "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), + "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), _ => { unreachable!("`pass_type` should only ever be `early` or `late`!"); }, From eb8ede7f659da6c07d1f475aff9b0d46e5b49a79 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 23 Aug 2020 11:29:06 +0200 Subject: [PATCH 0485/1110] Improve documentation related to the sync process --- CONTRIBUTING.md | 56 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f7b1e85ee9..631064cd92c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -189,6 +189,35 @@ Clippy in the `rust-lang/rust` repository. For general information about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. +### Patching git-subtree to work with big repos + +Currently there's a bug in `git-subtree` that prevents it from working properly +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stall. +Before continuing with the following steps, we need to manually apply that fix to +our local copy of `git-subtree`. + +You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. +Put this file under `/usr/lib/git-core` (taking a backup of the previous file) +and make sure it has the proper permissions: + +```bash +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +sudo chown root:root /usr/lib/git-core/git-subtree +``` + +_Note:_ The first time running `git subtree push` a cache has to be built. This +involves going through the complete Clippy history once. For this you have to +increase the stack limit though, which you can do with `ulimit -s 60000`. +Make sure to run the `ulimit` command from the same session you call git subtree. + +_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`. +This shell has a hardcoded recursion limit set to 1000. In order to make this process work, +you need to force the script to run `bash` instead. You can do this by editing the first +line of the `git-subtree` script and changing `sh` to `bash`. + +### Performing the sync + Here is a TL;DR version of the sync process (all of the following commands have to be run inside the `rust` directory): @@ -198,6 +227,7 @@ to be run inside the `rust` directory): # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust ``` + _Note:_ This will directly push to the remote repository. You can also push to your local copy by replacing the remote address with `/path/to/rust-clippy` directory. @@ -213,14 +243,28 @@ to be run inside the `rust` directory): 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Discord] channel.) -4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: + +### Syncing back changes in Clippy to [`rust-lang/rust`] + +To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back +in a bi-weekly basis if there's no urgent changes. This is done starting on the day of +the Rust stable release and then every two other weeks. That way we guarantee that +every feature in Clippy is available for 2 weeks in nightly, before it can get to beta. +For reference, the first sync following this cadence was performed the 2020-08-27. + +1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous +section if necessary. + +2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git checkout -b sync-from-clippy git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master ``` -5. Open a PR to [`rust-lang/rust`] +3. Open a PR to [`rust-lang/rust`] -Also, you may want to define remotes, so you don't have to type out the remote +### Defining remotes + +You may want to define remotes, so you don't have to type out the remote addresses on every sync. You can do this with the following commands (these commands still have to be run inside the `rust` directory): @@ -241,12 +285,6 @@ You can then sync with the remote names from above, e.g.: $ git subtree push -P src/tools/clippy clippy-local sync-from-rust ``` -_Note:_ The first time running `git subtree push` a cache has to be built. This -involves going through the complete Clippy history once. For this you have to -increase the stack limit though, which you can do with `ulimit -s 60000`. For -this to work, you will need the fix of `git subtree` available -[here][gitgitgadget-pr]. - [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 [subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust From 8776db9f6d5163ca40232336dc51ad77eeb2b5e5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 23 Aug 2020 12:05:34 +0200 Subject: [PATCH 0486/1110] Fix ICE in `repeat_once` lint --- clippy_lints/src/repeat_once.rs | 14 +++++++------- tests/ui/crashes/ice-5944.rs | 13 +++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/ui/crashes/ice-5944.rs diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 77c206002ea..c0890018d46 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -39,12 +39,12 @@ declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); impl<'tcx> LateLintPass<'tcx> for RepeatOnce { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind; if path.ident.name == sym!(repeat); - if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&args[1]); - if !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); + if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); if ty.is_str() { span_lint_and_sugg( cx, @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on str", "consider using `.to_string()` instead", - format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if ty.builtin_index().is_some() { @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on slice", "consider using `.to_vec()` instead", - format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on a string literal", "consider using `.clone()` instead", - format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs new file mode 100644 index 00000000000..5caf29c6197 --- /dev/null +++ b/tests/ui/crashes/ice-5944.rs @@ -0,0 +1,13 @@ +#![warn(clippy::repeat_once)] + +trait Repeat { + fn repeat(&self) {} +} + +impl Repeat for usize { + fn repeat(&self) {} +} + +fn main() { + let _ = 42.repeat(); +} From 91b200c62b7c464cb890c68230ab2d74605a60d0 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 23 Aug 2020 22:16:24 +1200 Subject: [PATCH 0487/1110] Fix fp in `borrow_interior_mutable_const` Fix false positive when referencing a field behind a pointer. --- clippy_lints/src/non_copy_const.rs | 15 +++++++- tests/ui/borrow_interior_mutable_const.rs | 35 ++++++++++++++++++- tests/ui/borrow_interior_mutable_const.stderr | 32 ++++++++--------- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 031d69e86a1..f1df634701d 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -211,8 +211,21 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { needs_check_adjustment = false; }, ExprKind::Field(..) => { - dereferenced_expr = parent_expr; needs_check_adjustment = true; + + // Check whether implicit dereferences happened; + // if so, no need to go further up + // because of the same reason as the `ExprKind::Unary` case. + if cx + .typeck_results() + .expr_adjustments(dereferenced_expr) + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(_))) + { + break; + } + + dereferenced_expr = parent_expr; }, ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index fef9f4f39f8..310769cd002 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -2,7 +2,7 @@ #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] use std::borrow::Cow; -use std::cell::Cell; +use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; @@ -30,6 +30,37 @@ impl Trait for u64 { const ATOMIC: AtomicUsize = AtomicUsize::new(9); } +// This is just a pointer that can be safely dereferended, +// it's semantically the same as `&'static T`; +// but it isn't allowed make a static reference from an arbitrary integer value at the moment. +// For more information, please see the issue #5918. +pub struct StaticRef { + ptr: *const T, +} + +impl StaticRef { + /// Create a new `StaticRef` from a raw pointer + /// + /// ## Safety + /// + /// Callers must pass in a reference to statically allocated memory which + /// does not overlap with other values. + pub const unsafe fn new(ptr: *const T) -> StaticRef { + StaticRef { ptr } + } +} + +impl std::ops::Deref for StaticRef { + type Target = T; + + fn deref(&self) -> &'static T { + unsafe { &*self.ptr } + } +} + +// use a tuple to make sure referencing a field behind a pointer isn't linted. +const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; + fn main() { ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability @@ -82,4 +113,6 @@ fn main() { assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. + + let _ = &CELL_REF.0; } diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index dc738064a17..1e0b3e4d20a 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:34:5 + --> $DIR/borrow_interior_mutable_const.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:35:16 + --> $DIR/borrow_interior_mutable_const.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:38:22 + --> $DIR/borrow_interior_mutable_const.rs:69:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:39:25 + --> $DIR/borrow_interior_mutable_const.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:40:27 + --> $DIR/borrow_interior_mutable_const.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:41:26 + --> $DIR/borrow_interior_mutable_const.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:52:14 + --> $DIR/borrow_interior_mutable_const.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:53:14 + --> $DIR/borrow_interior_mutable_const.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:54:19 + --> $DIR/borrow_interior_mutable_const.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:55:14 + --> $DIR/borrow_interior_mutable_const.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:56:13 + --> $DIR/borrow_interior_mutable_const.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:62:13 + --> $DIR/borrow_interior_mutable_const.rs:93:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:5 + --> $DIR/borrow_interior_mutable_const.rs:98:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:68:16 + --> $DIR/borrow_interior_mutable_const.rs:99:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:5 + --> $DIR/borrow_interior_mutable_const.rs:112:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:82:16 + --> $DIR/borrow_interior_mutable_const.rs:113:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ From b05077ea75f4585551650fc2d40a196ae437393d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 23 Aug 2020 22:20:55 +0200 Subject: [PATCH 0488/1110] Apply suggestions from PR review --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 631064cd92c..54777810abb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,7 +192,7 @@ For general information about `subtree`s in the Rust repository see [Rust's ### Patching git-subtree to work with big repos Currently there's a bug in `git-subtree` that prevents it from working properly -with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stall. +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale. Before continuing with the following steps, we need to manually apply that fix to our local copy of `git-subtree`. @@ -203,7 +203,7 @@ and make sure it has the proper permissions: ```bash sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -sudo chown root:root /usr/lib/git-core/git-subtree +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree ``` _Note:_ The first time running `git subtree push` a cache has to be built. This @@ -248,9 +248,11 @@ to be run inside the `rust` directory): To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back in a bi-weekly basis if there's no urgent changes. This is done starting on the day of -the Rust stable release and then every two other weeks. That way we guarantee that +the Rust stable release and then every other week. That way we guarantee that every feature in Clippy is available for 2 weeks in nightly, before it can get to beta. -For reference, the first sync following this cadence was performed the 2020-08-27. +For reference, the first sync following this cadence was performed the 2020-08-27. + +All of the following commands have to be run inside the `rust` directory. 1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous section if necessary. From b2c226679251bb795d786d7cdd11d2e2c9d0e685 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 18 Aug 2020 23:00:21 +0900 Subject: [PATCH 0489/1110] Fix FP for `redundant_closure_call` Visit the nested things like function body when checking closure call. --- clippy_lints/src/redundant_closure_call.rs | 17 +++++++++++------ tests/ui/redundant_closure_call_late.rs | 12 ++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8aa478ea2d6..d8fad217e5a 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -95,12 +95,17 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { - struct ClosureUsageCount<'tcx> { + fn count_closure_usage<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + block: &'tcx hir::Block<'_>, + path: &'tcx hir::Path<'tcx>, + ) -> usize { + struct ClosureUsageCount<'a, 'tcx> { + cx: &'a LateContext<'tcx>, path: &'tcx hir::Path<'tcx>, count: usize, }; - impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { @@ -117,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::None + hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } }; - let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(cx, block, path) == 1; then { span_lint( cx, diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index e29a1dce0c7..1f4864b7289 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -24,4 +24,16 @@ fn main() { let shadowed_closure = || 2; i = shadowed_closure(); i = shadowed_closure(); + + // Fix FP in #5916 + let mut x; + let create = || 2 * 2; + x = create(); + fun(move || { + x = create(); + }) +} + +fn fun(mut f: T) { + f(); } From 9fe0ac36a53dfba14f5f33f5bab2fd243fb2c18e Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 24 Aug 2020 10:11:53 +0900 Subject: [PATCH 0490/1110] Avoid period in lint message according to convention --- clippy_lints/src/redundant_closure_call.rs | 2 +- tests/ui/redundant_closure_call_early.stderr | 4 ++-- tests/ui/redundant_closure_call_fixable.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index d8fad217e5a..49cb2ffc4e3 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,7 +77,7 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 79f27663461..2735e41738f 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 644161d9f5d..afd704ef12a 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); From ce8915c85cebaa30e6846322e0ab44a4f4f9cb65 Mon Sep 17 00:00:00 2001 From: rail-rain <12975677+rail-rain@users.noreply.github.com> Date: Mon, 24 Aug 2020 19:08:38 +1200 Subject: [PATCH 0491/1110] typo Co-authored-by: Thibaud --- tests/ui/borrow_interior_mutable_const.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index 310769cd002..a414832bcd3 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -32,7 +32,7 @@ impl Trait for u64 { // This is just a pointer that can be safely dereferended, // it's semantically the same as `&'static T`; -// but it isn't allowed make a static reference from an arbitrary integer value at the moment. +// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. // For more information, please see the issue #5918. pub struct StaticRef { ptr: *const T, From 3d820f71fe820c0bb7a1204e28ce549407a937cc Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 24 Aug 2020 14:01:27 +0200 Subject: [PATCH 0492/1110] Fix incorrect suggestion when `clone_on_ref_ptr` is triggered in macros --- clippy_lints/src/methods/mod.rs | 13 +++++++------ tests/ui/unnecessary_clone.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_clone.stderr | 8 +++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 22942d9fb0c..4f0a533592c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2150,18 +2150,19 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; + let snippet = if in_macro(arg.span) { + snippet_with_macro_callsite(cx, arg.span, "_") + } else { + snippet(cx, arg.span, "_") + }; + span_lint_and_sugg( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!( - "{}::<{}>::clone(&{})", - caller_type, - subst.type_at(0), - snippet(cx, arg.span, "_") - ), + format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 2c9d4d39e6c..e785ac02feb 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -90,3 +90,21 @@ mod many_derefs { let _ = &encoded.clone(); } } + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + } +} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 113fab69009..5ffa6c4fd06 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -96,5 +96,11 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &<&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:108:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 12 previous errors From 680c68153bced747c90947e0265c92076edcb838 Mon Sep 17 00:00:00 2001 From: Bastian Date: Mon, 24 Aug 2020 16:31:51 +0200 Subject: [PATCH 0493/1110] Added a lint which corrects expressions like (a - b) < f32::EPSILON --- CHANGELOG.md | 1 + .../src/float_equality_without_abs.rs | 112 ++++++++++++++++++ clippy_lints/src/lib.rs | 5 + src/lintlist/mod.rs | 7 ++ tests/ui/float_equality_without_abs.rs | 31 +++++ tests/ui/float_equality_without_abs.stderr | 70 +++++++++++ 6 files changed, 226 insertions(+) create mode 100644 clippy_lints/src/float_equality_without_abs.rs create mode 100644 tests/ui/float_equality_without_abs.rs create mode 100644 tests/ui/float_equality_without_abs.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce63c0a157..a5da0f7b767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1498,6 +1498,7 @@ Released 2018-09-13 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs new file mode 100644 index 00000000000..c8e53ce6f95 --- /dev/null +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -0,0 +1,112 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or + /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// + /// **Why is this bad?** The code without `.abs()` is more likely to have a bug. + /// + /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is + /// technically unneccessary. However, it will make the code more robust and doesn't have any + /// large performance implications. If the abs call was deliberately left out for performance + /// reasons, it is probably better to state this explicitly in the code, which then can be done + /// with an allow. + /// + /// **Example:** + /// + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b) < f32::EPSILON + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b).abs() < f32::EPSILON + /// } + /// ``` + pub FLOAT_EQUALITY_WITHOUT_ABS, + correctness, + "float equality check without `.abs()`" +} + +declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); + +impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let lhs; + let rhs; + + // check if expr is a binary expression with a lt or gt operator + if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + match op.node { + BinOpKind::Lt => { + lhs = left; + rhs = right; + }, + BinOpKind::Gt => { + lhs = right; + rhs = left; + }, + _ => return, + }; + } else { + return; + } + + if_chain! { + + // left hand side is a substraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, + .. + }, + val_l, + val_r, + ) = lhs.kind; + + // right hand side matches either f32::EPSILON or f64::EPSILON + if let ExprKind::Path(ref epsilon_path) = rhs.kind; + if match_qpath(epsilon_path, &["f32", "EPSILON"]) || match_qpath(epsilon_path, &["f64", "EPSILON"]); + + // values of the substractions on the left hand side are of the type float + let t_val_l = cx.typeck_results().expr_ty(val_l); + let t_val_r = cx.typeck_results().expr_ty(val_r); + if let ty::Float(_) = t_val_l.kind; + if let ty::Float(_) = t_val_r.kind; + + then { + // get the snippet string + let lhs_string = snippet( + cx, + lhs.span, + "(...)", + ); + // format the suggestion + let suggestion = if lhs_string.starts_with('(') { + format!("{}.abs()", lhs_string) + } else { + format!("({}).abs()", lhs_string) + }; + // spans the lint + span_lint_and_sugg( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d45394ab8d2..f78de7a175f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -193,6 +193,7 @@ mod excessive_bools; mod exit; mod explicit_write; mod fallible_impl_from; +mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; @@ -549,6 +550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, + &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, &float_literal::EXCESSIVE_PRECISION, &float_literal::LOSSY_FLOAT_LITERAL, &floating_point_arithmetic::IMPRECISE_FLOPS, @@ -1093,6 +1095,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1268,6 +1271,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(&explicit_write::EXPLICIT_WRITE), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&float_literal::EXCESSIVE_PRECISION), LintId::of(&format::USELESS_FORMAT), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), @@ -1686,6 +1690,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(&eq_op::EQ_OP), LintId::of(&erasing_op::ERASING_OP), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&if_let_mutex::IF_LET_MUTEX), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c50c6b900ae..eadd2621a40 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -661,6 +661,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "float_equality_without_abs", + group: "correctness", + desc: "float equality check without `.abs()`", + deprecation: None, + module: "float_equality_without_abs", + }, Lint { name: "fn_address_comparisons", group: "correctness", diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs new file mode 100644 index 00000000000..d40fa00c315 --- /dev/null +++ b/tests/ui/float_equality_without_abs.rs @@ -0,0 +1,31 @@ +#![warn(clippy::float_equality_without_abs)] + +pub fn is_roughly_equal(a: f32, b: f32) -> bool { + (a - b) < f32::EPSILON +} + +pub fn main() { + // all errors + is_roughly_equal(1.0, 2.0); + let a = 0.05; + let b = 0.0500001; + + let _ = (a - b) < f32::EPSILON; + let _ = a - b < f32::EPSILON; + let _ = a - b.abs() < f32::EPSILON; + let _ = (a as f64 - b as f64) < f64::EPSILON; + let _ = 1.0 - 2.0 < f32::EPSILON; + + let _ = f32::EPSILON > (a - b); + let _ = f32::EPSILON > a - b; + let _ = f32::EPSILON > a - b.abs(); + let _ = f64::EPSILON > (a as f64 - b as f64); + let _ = f32::EPSILON > 1.0 - 2.0; + + // those are correct + let _ = (a - b).abs() < f32::EPSILON; + let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + + let _ = f32::EPSILON > (a - b).abs(); + let _ = f64::EPSILON > (a as f64 - b as f64).abs(); +} diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr new file mode 100644 index 00000000000..74b9078afe8 --- /dev/null +++ b/tests/ui/float_equality_without_abs.stderr @@ -0,0 +1,70 @@ +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:4:5 + | +LL | (a - b) < f32::EPSILON + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | + = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:13:13 + | +LL | let _ = (a - b) < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:14:13 + | +LL | let _ = a - b < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:15:13 + | +LL | let _ = a - b.abs() < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:16:13 + | +LL | let _ = (a as f64 - b as f64) < f64::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:17:13 + | +LL | let _ = 1.0 - 2.0 < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:19:13 + | +LL | let _ = f32::EPSILON > (a - b); + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:20:13 + | +LL | let _ = f32::EPSILON > a - b; + | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:21:13 + | +LL | let _ = f32::EPSILON > a - b.abs(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:22:13 + | +LL | let _ = f64::EPSILON > (a as f64 - b as f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:23:13 + | +LL | let _ = f32::EPSILON > 1.0 - 2.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: aborting due to 11 previous errors + From e9964f2e8a1903d46e576f67122e234f719a90f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 21 Aug 2020 02:03:57 +0200 Subject: [PATCH 0494/1110] stable_sort_primitive: print the type that is being sorted in the lint message --- clippy_lints/src/stable_sort_primitive.rs | 10 +++++---- clippy_lints/src/utils/mod.rs | 27 +++++++++++++++++++---- tests/ui/stable_sort_primitive.stderr | 14 ++++++------ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 22c49a20451..99e4b293ac6 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -86,6 +86,7 @@ struct LintDetection { slice_name: String, method: SortingKind, method_args: String, + slice_type: String, } fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { @@ -93,10 +94,10 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; if let Some(slice) = &args.get(0); if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); - if is_slice_of_primitives(cx, slice); + if let Some(slice_type) = is_slice_of_primitives(cx, slice); then { let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); - Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type }) } else { None } @@ -111,9 +112,10 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "used {} instead of {}", + "used {} instead of {} to sort primitive type `{}`", detection.method.stable_name(), - detection.method.unstable_name() + detection.method.unstable_name(), + detection.slice_type, ) .as_str(), "try", diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..25301d6dede 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1409,11 +1409,13 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Returns true iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function). -pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +/// Returns Option where String is a textual representation of the type encapsulated in the +/// slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and None otherwise. +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); - match expr_type.kind { + let expr_kind = &expr_type.kind; + let is_primitive = match expr_kind { ty::Slice(ref element_type) | ty::Ref( _, @@ -1424,7 +1426,24 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _, ) => is_recursively_primitive_type(element_type), _ => false, + }; + + if is_primitive { + // if we have wrappers like Array, Slice or Tuple, print these + // and get the type enclosed in the slice ref + match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind { + ty::Slice(..) => return Some("slice".into()), + ty::Array(..) => return Some("array".into()), + ty::Tuple(..) => return Some("tuple".into()), + _ => { + // is_recursively_primitive_type() should have taken care + // of the rest and we can rely on the type that is found + let refs_peeled = expr_type.peel_refs(); + return Some(refs_peeled.walk().last().unwrap().to_string()); + }, + } } + None } #[macro_export] diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b73012a4691..780389f32bc 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); From 91028326927266af5051b5d172dc3162d147a4d9 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 25 Aug 2020 05:53:28 +0200 Subject: [PATCH 0495/1110] Fix incorrect comment --- tests/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fmt.rs b/tests/fmt.rs index 3aff8741f60..ee3ce26ee40 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -7,7 +7,7 @@ fn fmt() { return; } - // Skip this test if rustup nightly is unavailable + // Skip this test if rustfmt nightly is unavailable let rustup_output = Command::new("rustup") .args(&["component", "list", "--toolchain", "nightly"]) .output() From 48d473691359f7c59d7348937f71de0e5aa4ae12 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 25 Aug 2020 05:53:28 +0200 Subject: [PATCH 0496/1110] Simplify fmt test by using the clippy dev alias --- tests/fmt.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/fmt.rs b/tests/fmt.rs index ee3ce26ee40..d529952bc69 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -19,12 +19,9 @@ fn fmt() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let dev_dir = root_dir.join("clippy_dev"); - let target_dir = root_dir.join("target"); - let target_dir = target_dir.to_str().unwrap(); let output = Command::new("cargo") - .current_dir(dev_dir) - .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"]) + .current_dir(root_dir) + .args(&["dev", "fmt", "--check"]) .output() .unwrap(); From c7dc9c3cf7dc8a810ed0456834cd162055847e9d Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 25 Aug 2020 05:53:28 +0200 Subject: [PATCH 0497/1110] Fix intermittent build error The fmt test will cause clippy dev to be compiled and run. The clippy dev dependencies are currently stored at `target/debug/deps` when this happens. This location sometimes causes conflicts with ui tests which result in the build error "thread 'compile_test' panicked at 'Found multiple rlibs for crate `regex` ...". This commit forces the clippy_dev dependencies to be stored under `clippy_dev/target/` which seems to resolve this problem. --- .cargo/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config b/.cargo/config index 2bad3b9c57f..e70da43ab47 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,6 +1,6 @@ [alias] uitest = "test --test compile-test" -dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" +dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" [build] rustflags = ["-Zunstable-options"] From 179df0bd15f9f866b4b3e275ed97aa8168a5b702 Mon Sep 17 00:00:00 2001 From: Bastian Date: Tue, 25 Aug 2020 13:41:39 +0200 Subject: [PATCH 0498/1110] Added F32::EPSILON and F64::EPSILON to paths.rs --- clippy_lints/src/float_equality_without_abs.rs | 4 ++-- clippy_lints/src/utils/paths.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index c8e53ce6f95..dc1c3bfc9ff 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { // right hand side matches either f32::EPSILON or f64::EPSILON if let ExprKind::Path(ref epsilon_path) = rhs.kind; - if match_qpath(epsilon_path, &["f32", "EPSILON"]) || match_qpath(epsilon_path, &["f64", "EPSILON"]); + if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); // values of the substractions on the left hand side are of the type float let t_val_l = cx.typeck_results().expr_ty(val_l); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index b6a1e0a6aa9..d44854aefe9 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -35,6 +35,8 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; +pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"]; +pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; From acc6b6ce07a5816cbfdaba7798edfc5b22ac976b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 25 Aug 2020 18:01:08 +0200 Subject: [PATCH 0499/1110] Fix typo --- tests/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fmt.rs b/tests/fmt.rs index d529952bc69..7616d8001e8 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -7,7 +7,7 @@ fn fmt() { return; } - // Skip this test if rustfmt nightly is unavailable + // Skip this test if nightly rustfmt is unavailable let rustup_output = Command::new("rustup") .args(&["component", "list", "--toolchain", "nightly"]) .output() From 3b1e5d6ff79f93e0215c6fb4c802167834ea4e15 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 25 Aug 2020 12:05:02 -0700 Subject: [PATCH 0500/1110] Re-enable len_zero for ranges now that `is_empty` is stable on them --- clippy_lints/src/len_zero.rs | 17 +---------------- tests/ui/len_zero.fixed | 8 -------- tests/ui/len_zero.rs | 8 -------- tests/ui/len_zero_ranges.fixed | 10 ++++++---- tests/ui/len_zero_ranges.rs | 10 ++++++---- tests/ui/len_zero_ranges.stderr | 10 ++++++++-- 6 files changed, 21 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e5daa30f8ca..b691d363d2f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -260,17 +260,6 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. - fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - higher::range(expr).map_or(false, |_| { - !cx.tcx - .features() - .declared_lib_features - .iter() - .any(|(name, _)| name.as_str() == "range_is_empty") - }) - } - /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { if let ty::AssocKind::Fn = item.kind { @@ -296,10 +285,6 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - if should_skip_range(cx, expr) { - return false; - } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index d81676a3d9f..1f3b8ac99b1 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index ecdba921a5d..dc21de0001b 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed index eee3db9b7d4..79781766242 100644 --- a/tests/ui/len_zero_ranges.fixed +++ b/tests/ui/len_zero_ranges.fixed @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).is_empty(); } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).is_empty(); + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs index be2e0f38fd1..a0eb51cc976 100644 --- a/tests/ui/len_zero_ranges.rs +++ b/tests/ui/len_zero_ranges.rs @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).len() == 0; } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).len() == 0; + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr index acb85f7100a..d0defb5a79e 100644 --- a/tests/ui/len_zero_ranges.stderr +++ b/tests/ui/len_zero_ranges.stderr @@ -1,10 +1,16 @@ error: length comparison to zero - --> $DIR/len_zero_ranges.rs:11:17 + --> $DIR/len_zero_ranges.rs:9:17 | LL | let _ = (0..42).len() == 0; | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` | = note: `-D clippy::len-zero` implied by `-D warnings` -error: aborting due to previous error +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:13:17 + | +LL | let _ = (0_u8..=42).len() == 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()` + +error: aborting due to 2 previous errors From 370fc45a0a2af502d43f0e1831def314e0580a92 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Tue, 25 Aug 2020 22:20:35 +0200 Subject: [PATCH 0501/1110] Update clippy_lints/src/methods/mod.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/methods/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4f0a533592c..1ef54d285f6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2150,11 +2150,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; - let snippet = if in_macro(arg.span) { - snippet_with_macro_callsite(cx, arg.span, "_") - } else { - snippet(cx, arg.span, "_") - }; + let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); span_lint_and_sugg( cx, From 9d18c24b56b6079fc0b66d93b8679c760cd364fb Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 26 Aug 2020 16:03:35 +0900 Subject: [PATCH 0502/1110] Fix typo --- clippy_lints/src/utils/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 25301d6dede..e6be5c4588f 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -574,7 +574,7 @@ pub fn snippet_block<'a, T: LintContext>( } /// Same as `snippet_block`, but adapts the applicability level by the rules of -/// `snippet_with_applicabiliy`. +/// `snippet_with_applicability`. pub fn snippet_block_with_applicability<'a, T: LintContext>( cx: &T, span: Span, @@ -1304,7 +1304,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// check if expr is calling method or function with #[must_use] attribyte +// check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { ExprKind::Call(ref path, _) => if_chain! { From 2d853148d72d49956052cf1cdb5f3be18c85a9fc Mon Sep 17 00:00:00 2001 From: Bastian Date: Wed, 26 Aug 2020 16:39:30 +0200 Subject: [PATCH 0503/1110] Changed the location of the suggestion as well as the way the suggestion is assembled --- .../src/float_equality_without_abs.rs | 30 ++++++------- tests/ui/float_equality_without_abs.stderr | 44 ++++++++++++++----- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index dc1c3bfc9ff..9ac5a45eb45 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,5 +1,6 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; use if_chain::if_chain; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -84,27 +85,24 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { if let ty::Float(_) = t_val_r.kind; then { - // get the snippet string - let lhs_string = snippet( - cx, - lhs.span, - "(...)", - ); + let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); // format the suggestion - let suggestion = if lhs_string.starts_with('(') { - format!("{}.abs()", lhs_string) - } else { - format!("({}).abs()", lhs_string) - }; + let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); // spans the lint - span_lint_and_sugg( + span_lint_and_then( cx, FLOAT_EQUALITY_WITHOUT_ABS, expr.span, "float equality check without `.abs()`", - "add `.abs()`", - suggestion, - Applicability::MaybeIncorrect, + | diag | { + diag.span_suggestion( + lhs.span, + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } ); } } diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr index 74b9078afe8..b34c8159da0 100644 --- a/tests/ui/float_equality_without_abs.stderr +++ b/tests/ui/float_equality_without_abs.stderr @@ -2,7 +2,9 @@ error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:4:5 | LL | (a - b) < f32::EPSILON - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` | = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` @@ -10,61 +12,81 @@ error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:13:13 | LL | let _ = (a - b) < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:14:13 | LL | let _ = a - b < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -----^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:15:13 | LL | let _ = a - b.abs() < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + | -----------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:16:13 | LL | let _ = (a as f64 - b as f64) < f64::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:17:13 | LL | let _ = 1.0 - 2.0 < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + | ---------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:19:13 | LL | let _ = f32::EPSILON > (a - b); - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | ^^^^^^^^^^^^^^^------- + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:20:13 | LL | let _ = f32::EPSILON > a - b; - | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | ^^^^^^^^^^^^^^^----- + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:21:13 | LL | let _ = f32::EPSILON > a - b.abs(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + | ^^^^^^^^^^^^^^^----------- + | | + | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:22:13 | LL | let _ = f64::EPSILON > (a as f64 - b as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + | ^^^^^^^^^^^^^^^--------------------- + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:23:13 | LL | let _ = f32::EPSILON > 1.0 - 2.0; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + | ^^^^^^^^^^^^^^^--------- + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` error: aborting due to 11 previous errors From edc05da57d4ad5ab19b5ca64e80e359e487ab2d0 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 27 Aug 2020 10:23:21 +1200 Subject: [PATCH 0504/1110] Fix the wrong use of `snippet_with_applicability` This includes a workaround of the issue #5822, the cause of this little mistake. --- clippy_lints/src/write.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 5f88dcb188a..e653240d049 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -297,13 +297,14 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable), - ); + // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed + #[allow(clippy::option_if_let_else)] + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, From 91024f1fdee2d2c2febfc7c76127d68d2b6e629e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Wed, 26 Aug 2020 16:31:49 -0700 Subject: [PATCH 0505/1110] Add new lint to prevent usage of unwrap in fns that return result --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/unwrap_in_result.rs | 140 +++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/unwrap_in_result.rs | 44 +++++++++ tests/ui/unwrap_in_result.stderr | 41 ++++++++ 6 files changed, 237 insertions(+) create mode 100644 clippy_lints/src/unwrap_in_result.rs create mode 100644 tests/ui/unwrap_in_result.rs create mode 100644 tests/ui/unwrap_in_result.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index a5da0f7b767..137b561028a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1778,6 +1778,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f78de7a175f..577ce6523b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -314,6 +314,7 @@ mod unused_io_amount; mod unused_self; mod unused_unit; mod unwrap; +mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; @@ -850,6 +851,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, + &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, @@ -1094,6 +1096,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); @@ -1133,6 +1136,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs new file mode 100644 index 00000000000..1c7e62ecd3d --- /dev/null +++ b/clippy_lints/src/unwrap_in_result.rs @@ -0,0 +1,140 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` + /// + /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. + /// + /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors. + /// + /// **Example:** + /// Before: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .expect("cannot divide the input by three"); + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// After: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .map_err(|e| format!("cannot divide the input by three: {}", e))?; + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + pub UNWRAP_IN_RESULT, + restriction, + "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" +} + +declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindExpectUnwrap<'a, 'tcx> { + lcx: &'a LateContext<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // check for `expect` + if let Some(arglists) = method_chain_args(expr, &["expect"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let mut fpu = FindExpectUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + UNWRAP_IN_RESULT, + impl_span, + "used unwrap or expect in a function that returns result or option", + move |diag| { + diag.help( + "unwrap and expect should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "potential non-recoverable error(s)"); + }); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index eadd2621a40..687fac7baa8 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2516,6 +2516,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "unused_unit", }, + Lint { + name: "unwrap_in_result", + group: "restriction", + desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`", + deprecation: None, + module: "unwrap_in_result", + }, Lint { name: "unwrap_used", group: "restriction", diff --git a/tests/ui/unwrap_in_result.rs b/tests/ui/unwrap_in_result.rs new file mode 100644 index 00000000000..2aa842adc85 --- /dev/null +++ b/tests/ui/unwrap_in_result.rs @@ -0,0 +1,44 @@ +#![warn(clippy::unwrap_in_result)] + +struct A; + +impl A { + // should not be detected + fn good_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i_result = i_str.parse::(); + match i_result { + Err(_e) => Err("Not a number".to_string()), + Ok(i) => { + if i % 3 == 0 { + return Ok(true); + } + Err("Number is not divisible by 3".to_string()) + }, + } + } + + // should be detected + fn bad_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i = i_str.parse::().unwrap(); + if i % 3 == 0 { + Ok(true) + } else { + Err("Number is not divisible by 3".to_string()) + } + } + + fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().expect("not a number"); + if i % 3 == 0 { + return Some(true); + } + None + } +} + +fn main() { + A::bad_divisible_by_3("3".to_string()); + A::good_divisible_by_3("3".to_string()); +} diff --git a/tests/ui/unwrap_in_result.stderr b/tests/ui/unwrap_in_result.stderr new file mode 100644 index 00000000000..56bc2f2d1c0 --- /dev/null +++ b/tests/ui/unwrap_in_result.stderr @@ -0,0 +1,41 @@ +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:22:5 + | +LL | / fn bad_divisible_by_3(i_str: String) -> Result { +LL | | // checks whether a string represents a number divisible by 3 +LL | | let i = i_str.parse::().unwrap(); +LL | | if i % 3 == 0 { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::unwrap-in-result` implied by `-D warnings` + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:24:17 + | +LL | let i = i_str.parse::().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:32:5 + | +LL | / fn example_option_expect(i_str: String) -> Option { +LL | | let i = i_str.parse::().expect("not a number"); +LL | | if i % 3 == 0 { +LL | | return Some(true); +LL | | } +LL | | None +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:33:17 + | +LL | let i = i_str.parse::().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From baf62e7a38854ff6a0039ddccb124ff329a32143 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 27 Aug 2020 14:39:09 +0200 Subject: [PATCH 0506/1110] Update changelog to beta 1.47 --- CHANGELOG.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..34d48821023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,113 @@ document. ## Unreleased / In Rust Nightly -[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master) +[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master) + +## Rust 1.47 + +Current beta, release 2020-10-08 + +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) + +### New lints + +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848) +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852) +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694) +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737) +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841) +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773) +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825) +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869) +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769) +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809) +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750) +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301) + +### Moves and Deprecations + +* Deprecate [`regex_macro`] lint + [#5760](https://github.com/rust-lang/rust-clippy/pull/5760) +* Move [`range_minus_one`] to `pedantic` + [#5752](https://github.com/rust-lang/rust-clippy/pull/5752) + +### Enhancements + +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls + [#5837](https://github.com/rust-lang/rust-clippy/pull/5837) +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting + [#5811](https://github.com/rust-lang/rust-clippy/pull/5811) +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`] + [#5443](https://github.com/rust-lang/rust-clippy/pull/5443) +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`] + [#5701](https://github.com/rust-lang/rust-clippy/pull/5701) +* Make it possible to allow [`unsafe_derive_deserialize`] + [#5870](https://github.com/rust-lang/rust-clippy/pull/5870) +* Catch `ord.min(a).max(b)` where a < b in [`min_max`] + [#5871](https://github.com/rust-lang/rust-clippy/pull/5871) +* Make [`clone_on_copy`] suggestion machine applicable + [#5745](https://github.com/rust-lang/rust-clippy/pull/5745) +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them + [#5961](https://github.com/rust-lang/rust-clippy/pull/5961) + +### False Positive Fixes + +* Avoid triggering [`or_fun_call`] with const fns that take no arguments + [#5889](https://github.com/rust-lang/rust-clippy/pull/5889) +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls + [#5800](https://github.com/rust-lang/rust-clippy/pull/5800) +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`] + [#5824](https://github.com/rust-lang/rust-clippy/pull/5824) +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`] + [#5771](https://github.com/rust-lang/rust-clippy/pull/5771) +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled + [#5758](https://github.com/rust-lang/rust-clippy/pull/5758) +* Avoid linting if key borrows in [`unnecessary_sort_by`] + [#5756](https://github.com/rust-lang/rust-clippy/pull/5756) +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`] + [#5857](https://github.com/rust-lang/rust-clippy/pull/5857) +* Take input lifetimes into account in `manual_async_fn` + [#5859](https://github.com/rust-lang/rust-clippy/pull/5859) +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option + [#5761](https://github.com/rust-lang/rust-clippy/pull/5761) +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation + [#5820](https://github.com/rust-lang/rust-clippy/pull/5820) + +### Suggestion Fixes/Improvements + +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet + [#5788](https://github.com/rust-lang/rust-clippy/pull/5788) +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`] + [#5846](https://github.com/rust-lang/rust-clippy/pull/5846) +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`] + [#5793](https://github.com/rust-lang/rust-clippy/pull/5793) +* Drop borrow operator in suggestions of [`redundant_pattern_matching`] + [#5815](https://github.com/rust-lang/rust-clippy/pull/5815) +* Add suggestion for [`iter_skip_next`] + [#5843](https://github.com/rust-lang/rust-clippy/pull/5843) +* Improve [`collapsible_if`] fix suggestion + [#5732](https://github.com/rust-lang/rust-clippy/pull/5732) + +### ICE Fixes + +* Fix ICE caused by [`needless_collect`] + [#5877](https://github.com/rust-lang/rust-clippy/pull/5877) +* Fix ICE caused by [`unnested_or_patterns`] + [#5784](https://github.com/rust-lang/rust-clippy/pull/5784) + +### Documentation Improvements + +* Fix grammar of [`await_holding_lock`] documentation + [#5748](https://github.com/rust-lang/rust-clippy/pull/5748) + +### Others + +* Make lints adhere to the rustc dev guide + [#5888](https://github.com/rust-lang/rust-clippy/pull/5888) ## Rust 1.46 -Current beta, release 2020-08-27 +Current stable, released 2020-08-27 [7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) @@ -72,7 +174,7 @@ Current beta, release 2020-08-27 ## Rust 1.45 -Current stable, released 2020-07-16 +Released 2020-07-16 [891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1) From 04bff17668be1305d9efe235665a32727ff3e0b5 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 27 Aug 2020 23:37:47 +0900 Subject: [PATCH 0507/1110] Fix FP in `to_string_in_display` Don't emit a lint when `.to_string()` on anything that is not `self` --- clippy_lints/src/to_string_in_display.rs | 32 ++++++++++++++++++++---- tests/ui/to_string_in_display.rs | 14 +++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 4b6a0a6a0c9..006d7a3a12d 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,6 +1,7 @@ -use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, Item, ItemKind}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -45,11 +46,15 @@ declare_clippy_lint! { #[derive(Default)] pub struct ToStringInDisplay { in_display_impl: bool, + self_hir_id: Option, } impl ToStringInDisplay { pub fn new() -> Self { - Self { in_display_impl: false } + Self { + in_display_impl: false, + self_hir_id: None, + } } } @@ -65,16 +70,33 @@ impl LateLintPass<'_> for ToStringInDisplay { fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if is_display_impl(cx, item) { self.in_display_impl = false; + self.self_hir_id = None; + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + if_chain! { + if self.in_display_impl; + if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + let body = cx.tcx.hir().body(*body_id); + if !body.params.is_empty(); + then { + let self_param = &body.params[0]; + self.self_hir_id = Some(self_param.pat.hir_id); + } } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind; + if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if match_trait_method(cx, expr, &paths::TO_STRING); if self.in_display_impl; - + if let ExprKind::Path(ref qpath) = args[0].kind; + if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id); + if let Some(self_hir_id) = self.self_hir_id; + if hir_id == self_hir_id; then { span_lint( cx, diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs index 3b46324704e..eb8105c6b6d 100644 --- a/tests/ui/to_string_in_display.rs +++ b/tests/ui/to_string_in_display.rs @@ -44,6 +44,20 @@ impl fmt::Display for C { } } +enum D { + E(String), + F, +} + +impl std::fmt::Display for D { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::E(string) => write!(f, "E {}", string.to_string()), + Self::F => write!(f, "F"), + } + } +} + fn main() { let a = A; a.to_string(); From 2a3ee5fa854b49530008582900c6ea7fac120d1c Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 7 Jul 2020 22:47:32 +0200 Subject: [PATCH 0508/1110] Fix FP in `new_ret_no_self`: trigger in trait def instead of impl block --- clippy_lints/src/methods/mod.rs | 58 ++++++++++++-- tests/ui/new_ret_no_self.rs | 130 ++++++++++++++++++++++++++++++++ tests/ui/new_ret_no_self.stderr | 30 +++++++- 3 files changed, 212 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1ef54d285f6..8e91cbb3cdf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,6 +15,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{FnRetTy, FnSig, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -28,11 +29,11 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + is_ctor_or_promotable_const_function, is_expn_of, is_self_ty, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1631,6 +1632,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1670,6 +1676,48 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); + if !item.span.from_expansion(); + if item.ident.name == sym!(new); + if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; + if let FnRetTy::Return(ret_ty) = &decl.output; + + then { + let mut visitor = HasSelfVisitor { has_self_ty: false }; + visitor.visit_ty(ret_ty); + if !visitor.has_self_ty { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } + } + } + } +} + +struct HasSelfVisitor { + pub has_self_ty: bool, +} + +impl<'tcx> intravisit::Visitor<'tcx> for HasSelfVisitor { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + if is_self_ty(ty) { + self.has_self_ty = true; + } else { + intravisit::walk_ty(self, ty); + } + } + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } } /// Checks for the `OR_FUN_CALL` lint. diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index 2c2d1e27589..e98360ea691 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -210,3 +210,133 @@ impl<'a> WithLifetime<'a> { unimplemented!(); } } + +mod issue5435 { + struct V; + + pub trait TraitRetSelf { + // should not trigger lint + fn new() -> Self; + } + + pub trait TraitRet { + // should trigger lint as we are in trait definition + fn new() -> String; + } + pub struct StructRet; + impl TraitRet for StructRet { + // should not trigger lint as we are in the impl block + fn new() -> String { + unimplemented!(); + } + } + + pub trait TraitRet2 { + // should trigger lint + fn new(_: String) -> String; + } + + trait TupleReturnerOk { + // should not trigger lint + fn new() -> (Self, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + fn new() -> (u32, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + fn new() -> (Self, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerBad { + // should trigger lint + fn new() -> (u32, u32) { + unimplemented!(); + } + } + + trait MutPointerReturnerOk { + // should not trigger lint + fn new() -> *mut Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerOk2 { + // should not trigger lint + fn new() -> *const Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerBad { + // should trigger lint + fn new() -> *mut V { + unimplemented!(); + } + } + + trait GenericReturnerOk { + // should not trigger lint + fn new() -> Option + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk { + // should not trigger lint + fn new() -> (Option, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk2 { + // should not trigger lint + fn new() -> ((Self, u32), u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk3 { + // should not trigger lint + fn new() -> Option<(Self, u32)> + where + Self: Sized, + { + unimplemented!(); + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index dd5a24bcbe7..8217bc6187f 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -48,5 +48,33 @@ LL | | unimplemented!(); LL | | } | |_____^ -error: aborting due to 6 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:224:9 + | +LL | fn new() -> String; + | ^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:236:9 + | +LL | fn new(_: String) -> String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:271:9 + | +LL | / fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:298:9 + | +LL | / fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: aborting due to 10 previous errors From 3cb75c2e5cdd4f450f2974c5e052d569674d95fd Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 25 Aug 2020 09:16:08 +0200 Subject: [PATCH 0509/1110] Remove expansion restriction + fix doc and tests naming --- clippy_lints/src/methods/mod.rs | 34 +++++++++++++++++++++++---------- tests/ui/new_ret_no_self.rs | 6 +++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8e91cbb3cdf..2388310628f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -725,6 +725,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** + /// In an impl block: /// ```rust /// # struct Foo; /// # struct NotAFoo; @@ -737,25 +738,40 @@ declare_clippy_lint! { /// /// ```rust /// # struct Foo; - /// # struct FooError; + /// struct Bar(Foo); /// impl Foo { - /// // Good. Return type contains `Self` - /// fn new() -> Result { - /// # Ok(Foo) + /// // Bad. The type name must contain `Self` + /// fn new() -> Bar { + /// # Bar(Foo) /// } /// } /// ``` /// /// ```rust /// # struct Foo; - /// struct Bar(Foo); + /// # struct FooError; /// impl Foo { - /// // Bad. The type name must contain `Self`. - /// fn new() -> Bar { - /// # Bar(Foo) + /// // Good. Return type contains `Self` + /// fn new() -> Result { + /// # Ok(Foo) /// } /// } /// ``` + /// + /// Or in a trait definition: + /// ```rust + /// pub trait Trait { + /// // Bad. The type name must contain `Self` + /// fn new(); + /// } + /// ``` + /// + /// ```rust + /// pub trait Trait { + /// // Good. Return type contains `Self` + /// fn new() -> Self; + /// } + /// ``` pub NEW_RET_NO_SELF, style, "not returning type containing `Self` in a `new` method" @@ -1679,8 +1695,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); - if !item.span.from_expansion(); if item.ident.name == sym!(new); if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; if let FnRetTy::Return(ret_ty) = &decl.output; diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index e98360ea691..e82873629a5 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -137,9 +137,9 @@ impl MutPointerReturnerOk { } } -struct MutPointerReturnerOk2; +struct ConstPointerReturnerOk2; -impl MutPointerReturnerOk2 { +impl ConstPointerReturnerOk2 { // should not trigger lint pub fn new() -> *const Self { unimplemented!(); @@ -283,7 +283,7 @@ mod issue5435 { } } - trait MutPointerReturnerOk2 { + trait ConstPointerReturnerOk2 { // should not trigger lint fn new() -> *const Self where From 504612622f2801b43dbe3e6788d2404d394376df Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 27 Aug 2020 18:24:59 +0200 Subject: [PATCH 0510/1110] Merge logic of looking for `Self` type --- clippy_lints/src/methods/mod.rs | 62 +++++++++------------------------ clippy_lints/src/utils/mod.rs | 11 +++++- 2 files changed, 26 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2388310628f..63e0c183113 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,12 +15,11 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FnRetTy, FnSig, TraitItem, TraitItemKind}; +use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -28,8 +27,8 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_self_ty, is_type_diagnostic_item, iter_input_pats, + contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, + is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, @@ -1656,16 +1655,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); - let contains_self_ty = |ty: Ty<'tcx>| { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty), - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) - }; - // walk the return type and check for Self (this does not check associated types) - if contains_self_ty(ret_ty) { + if contains_ty(ret_ty, self_ty) { return; } @@ -1675,7 +1666,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates { if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self - if contains_self_ty(projection_predicate.ty) { + if contains_ty(projection_predicate.ty, self_ty) { return; } } @@ -1696,44 +1687,23 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { if item.ident.name == sym!(new); - if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; - if let FnRetTy::Return(ret_ty) = &decl.output; + if let TraitItemKind::Fn(_, _) = item.kind; + let ret_ty = return_ty(cx, item.hir_id); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + if !contains_ty(ret_ty, self_ty); then { - let mut visitor = HasSelfVisitor { has_self_ty: false }; - visitor.visit_ty(ret_ty); - if !visitor.has_self_ty { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); - } + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); } } } } -struct HasSelfVisitor { - pub has_self_ty: bool, -} - -impl<'tcx> intravisit::Visitor<'tcx> for HasSelfVisitor { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { - if is_self_ty(ty) { - self.has_self_ty = true; - } else { - intravisit::walk_ty(self, ty); - } - } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e6be5c4588f..07ec59f452a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -42,7 +42,8 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; @@ -866,6 +867,14 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(&ret_ty) } +/// Walk into `ty` and returns `true` if any inner type is the same as `other_ty` +pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind { From f3ccbef2af24d5d83f82f1fb50bd97a9b75e609f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 01:40:02 +0200 Subject: [PATCH 0511/1110] unit-arg - pr comments --- clippy_lints/src/types.rs | 41 ++++++++++---- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/unit_arg.rs | 8 ++- tests/ui/unit_arg.stderr | 79 ++++++++++++++------------- tests/ui/unit_arg_empty_blocks.stderr | 11 ++-- 5 files changed, 88 insertions(+), 53 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3f5b3a5bcd5..16e48d91916 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, }; declare_clippy_lint! { @@ -802,6 +802,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } +#[allow(clippy::too_many_lines)] fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -856,18 +857,38 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); + let indent = indent_of(cx, expr.span).unwrap_or(0); - if let Some(mut sugg) = snippet_opt(cx, expr.span) { - arg_snippets.iter().for_each(|arg| { - sugg = sugg.replacen(arg, "()", 1); - }); - sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + if let Some(expr_str) = snippet_opt(cx, expr.span) { + let expr_with_replacements = arg_snippets + .iter() + .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); + + // expr is not in a block statement or result expression position, wrap in a block let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { - // expr is not in a block statement or result expression position, wrap in a block - sugg = format!("{{ {} }}", sugg); + let wrap_in_block = + !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); + + let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; + let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); + stmts_and_call.push(expr_with_replacements); + let mut stmts_and_call_str = stmts_and_call + .into_iter() + .enumerate() + .map(|(i, v)| { + let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; + trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() + }) + .collect::>() + .join(";\n"); + + if wrap_in_block { + stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; + stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); } + let sugg = stmts_and_call_str; + if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( &format!("use {}unit literal{} instead", singular, plural), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..d20b33c4a1d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -662,7 +662,7 @@ pub fn expr_block<'a, T: LintContext>( /// Trim indentation from a multiline string with possibility of ignoring the /// first line. -fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { +pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); trim_multiline_inner(s_tab, ignore_first, indent, ' ') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2e2bd054e42..fec115ff29d 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,11 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] +#![allow( + clippy::no_effect, + unused_must_use, + unused_variables, + clippy::unused_unit, + clippy::or_fun_call +)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 2a0cc1f18e2..90fee3aab23 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:23:5 + --> $DIR/unit_arg.rs:29:5 | LL | / foo({ LL | | 1; @@ -15,22 +15,24 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:26:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); foo(()); - | ^^^^^^^^^^^^^^^ +LL | foo(1); +LL | foo(()); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:33:5 | LL | / foo({ LL | | foo(1); @@ -47,11 +49,12 @@ help: or move the expression in front of the call and replace it with the unit l LL | { LL | foo(1); LL | foo(2); -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:38:5 | LL | / b.bar({ LL | | 1; @@ -66,22 +69,25 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; b.bar(()); +LL | }; +LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:41:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_multiple_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_multiple_units((), ()); + | error: passing unit values to a function - --> $DIR/unit_arg.rs:36:5 + --> $DIR/unit_arg.rs:42:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -95,14 +101,16 @@ LL | foo(2) | help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); { +LL | foo(0); +LL | { LL | foo(1); LL | foo(2); -LL | }; taking_multiple_units((), ()); +LL | }; +LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:46:5 | LL | / taking_multiple_units( LL | | { @@ -124,53 +132,50 @@ LL | foo(3) help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; { -LL | foo(2); -LL | foo(3); +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); ... -error: use of `or` followed by a function call - --> $DIR/unit_arg.rs:51:10 - | -LL | None.or(Some(foo(2))); - | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` - | - = note: `-D clippy::or-fun-call` implied by `-D warnings` - error: passing a unit value to a function - --> $DIR/unit_arg.rs:51:13 + --> $DIR/unit_arg.rs:57:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | None.or({ foo(2); Some(()) }); - | ^^^^^^^^^^^^^^^^^^^^ +LL | None.or({ +LL | foo(2); +LL | Some(()) +LL | }); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:54:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(()); foo(()) +LL | foo(()); +LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:93:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); Some(()) +LL | foo(1); +LL | Some(()) | -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 4cbbc8b8cd4..456b12a2c6b 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -24,8 +24,9 @@ LL | taking_two_units({}, foo(0)); | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); taking_two_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | taking_two_units((), ()); + | error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -35,8 +36,10 @@ LL | taking_three_units({}, foo(0), foo(1)); | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_three_units((), (), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_three_units((), (), ()); + | error: aborting due to 4 previous errors From 459969f88ff95c94b7b34043a7f0e13de91de4f8 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:18:05 -0700 Subject: [PATCH 0512/1110] added restriction lint that prohibits the usage of unimplemented, unreachable or panic in a function of type result or option --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/panic_in_result.rs | 100 ++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/panic_in_result.rs | 62 ++++++++++++++++ tests/ui/panic_in_result.stderr | 105 ++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+) create mode 100644 clippy_lints/src/panic_in_result.rs create mode 100644 tests/ui/panic_in_result.rs create mode 100644 tests/ui/panic_in_result.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..7af3b666cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,6 +1651,7 @@ Released 2018-09-13 [`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 +[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..b70d126af5b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,6 +267,7 @@ mod open_options; mod option_env_unwrap; mod option_if_let_else; mod overflow_check_conditional; +mod panic_in_result; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; @@ -747,6 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, + &panic_in_result::PANIC_IN_RESULT, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::TODO, @@ -1086,6 +1088,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); + store.register_late_pass(|| box panic_in_result::PanicInResult); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1128,6 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(&panic_in_result::PANIC_IN_RESULT), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs new file mode 100644 index 00000000000..3a71a0db6fe --- /dev/null +++ b/clippy_lints/src/panic_in_result.rs @@ -0,0 +1,100 @@ +use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. + /// + /// **Why is this bad?** For some codebases, + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn option_with_panic() -> Option // should emit lint + /// { + /// panic!("error"); + /// } + /// ``` + + pub PANIC_IN_RESULT, + restriction, + "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` " +} + +declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for PanicInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindPanicUnimplementedUnreachable { + result: Vec, +} + +impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if is_expn_of(expr.span, "unimplemented").is_some() { + self.result.push(expr.span); + } else if is_expn_of(expr.span, "unreachable").is_some() { + self.result.push(expr.span); + } else if is_expn_of(expr.span, "panic").is_some() { + self.result.push(expr.span); + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let mut fpu = FindPanicUnimplementedUnreachable { + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT, + impl_span, + "used unimplemented, unreachable or panic in a function that returns result or option", + move |diag| { + diag.help( + "unimplemented, unreachable or panic should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "will cause the application to crash."); + }); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..ad57146048e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1704,6 +1704,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "panic_unimplemented", }, + Lint { + name: "panic_in_result", + group: "restriction", + desc: "default lint description", + deprecation: None, + module: "panic_in_result", + }, Lint { name: "panic_params", group: "style", diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs new file mode 100644 index 00000000000..21e9efca87b --- /dev/null +++ b/tests/ui/panic_in_result.rs @@ -0,0 +1,62 @@ +#![warn(clippy::panic_in_result)] + +struct A; + +impl A { + fn result_with_panic() -> Result // should emit lint + { + panic!("error"); + } + + fn result_with_unimplemented() -> Result // should emit lint + { + unimplemented!(); + } + + fn result_with_unreachable() -> Result // should emit lint + { + unreachable!(); + } + + fn option_with_unreachable() -> Option // should emit lint + { + unreachable!(); + } + + fn option_with_unimplemented() -> Option // should emit lint + { + unimplemented!(); + } + + fn option_with_panic() -> Option // should emit lint + { + panic!("error"); + } + + fn other_with_panic() // should not emit lint + { + panic!(""); + } + + fn other_with_unreachable() // should not emit lint + { + unreachable!(); + } + + fn other_with_unimplemented() // should not emit lint + { + unimplemented!(); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + Ok(true) + } + + fn option_without_banned_functions() -> Option // should not emit lint + { + Some(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr new file mode 100644 index 00000000000..74273bd9abb --- /dev/null +++ b/tests/ui/panic_in_result.stderr @@ -0,0 +1,105 @@ +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:6:5 + | +LL | / fn result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result` implied by `-D warnings` + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:8:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:11:5 + | +LL | / fn result_with_unimplemented() -> Result // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:13:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:16:5 + | +LL | / fn result_with_unreachable() -> Result // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:18:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:21:5 + | +LL | / fn option_with_unreachable() -> Option // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:23:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:26:5 + | +LL | / fn option_with_unimplemented() -> Option // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:28:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:31:5 + | +LL | / fn option_with_panic() -> Option // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:33:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + From ceab1a9167655eba9f9556f8766f8702e49dfef3 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:19:24 -0700 Subject: [PATCH 0513/1110] removed unnecessary comment --- clippy_lints/src/panic_in_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 3a71a0db6fe..4e08c991bd0 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// fn option_with_panic() -> Option // should emit lint + /// fn option_with_panic() -> Option /// { /// panic!("error"); /// } From 8462cce96081b87eba7a5bc89130a1a09fe1f6d0 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:22:37 -0700 Subject: [PATCH 0514/1110] edited documentation --- clippy_lints/src/panic_in_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 4e08c991bd0..239b4bdbdb1 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -9,7 +9,7 @@ use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. /// - /// **Why is this bad?** For some codebases, + /// **Why is this bad?** For some codebases, it is desirable for functions of type option/result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. /// /// **Known problems:** None. /// From b2d8ca9a766703469178ea37d4d46067bb6fa926 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:30:49 -0700 Subject: [PATCH 0515/1110] ran cargo dev update lints --- src/lintlist/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ad57146048e..b4f20ca7f14 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1707,7 +1707,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "panic_in_result", group: "restriction", - desc: "default lint description", + desc: "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` ", deprecation: None, module: "panic_in_result", }, From b006522393a3c3c2656e1ccdfbb0076ff1bd7e99 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:55:23 -0700 Subject: [PATCH 0516/1110] added lint for todo and removed option --- clippy_lints/src/panic_in_result.rs | 26 ++++++------- src/lintlist/mod.rs | 2 +- tests/ui/panic_in_result.rs | 24 ++++-------- tests/ui/panic_in_result.stderr | 60 +++++++---------------------- 4 files changed, 33 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 239b4bdbdb1..2901f393fc6 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -7,16 +7,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type option/result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// fn option_with_panic() -> Option + /// fn result_with_panic() -> Result /// { /// panic!("error"); /// } @@ -24,7 +24,7 @@ declare_clippy_lint! { pub PANIC_IN_RESULT, restriction, - "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " } declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); @@ -35,8 +35,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { // first check if it's a method or function if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)); then { lint_impl_body(cx, impl_item.span, impl_item); } @@ -55,14 +54,13 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if is_expn_of(expr.span, "unimplemented").is_some() { - self.result.push(expr.span); - } else if is_expn_of(expr.span, "unreachable").is_some() { - self.result.push(expr.span); - } else if is_expn_of(expr.span, "panic").is_some() { + if is_expn_of(expr.span, "unimplemented").is_some() + || is_expn_of(expr.span, "unreachable").is_some() + || is_expn_of(expr.span, "panic").is_some() + || is_expn_of(expr.span, "todo").is_some() + { self.result.push(expr.span); } - // and check sub-expressions intravisit::walk_expr(self, expr); } @@ -88,10 +86,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc cx, PANIC_IN_RESULT, impl_span, - "used unimplemented, unreachable or panic in a function that returns result or option", + "used unimplemented, unreachable, todo or panic in a function that returns result", move |diag| { diag.help( - "unimplemented, unreachable or panic should not be used in a function that returns result or option" ); + "unimplemented, unreachable, todo or panic should not be used in a function that returns result" ); diag.span_note(fpu.result, "will cause the application to crash."); }); } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b4f20ca7f14..1f56c56f081 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1707,7 +1707,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "panic_in_result", group: "restriction", - desc: "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` ", + desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ", deprecation: None, module: "panic_in_result", }, diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs index 21e9efca87b..056778995a4 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result.rs @@ -18,19 +18,9 @@ impl A { unreachable!(); } - fn option_with_unreachable() -> Option // should emit lint + fn result_with_todo() -> Result // should emit lint { - unreachable!(); - } - - fn option_with_unimplemented() -> Option // should emit lint - { - unimplemented!(); - } - - fn option_with_panic() -> Option // should emit lint - { - panic!("error"); + todo!("Finish this"); } fn other_with_panic() // should not emit lint @@ -48,15 +38,15 @@ impl A { unimplemented!(); } + fn other_with_todo() // should not emit lint + { + todo!("finish this") + } + fn result_without_banned_functions() -> Result // should not emit lint { Ok(true) } - - fn option_without_banned_functions() -> Option // should not emit lint - { - Some(true) - } } fn main() {} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr index 74273bd9abb..3b9ac69f20d 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result.stderr @@ -1,4 +1,4 @@ -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:8:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:13:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:18:9 | @@ -50,56 +50,22 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:21:5 | -LL | / fn option_with_unreachable() -> Option // should emit lint +LL | / fn result_with_todo() -> Result // should emit lint LL | | { -LL | | unreachable!(); +LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:23:9 | -LL | unreachable!(); - | ^^^^^^^^^^^^^^^ +LL | todo!("Finish this"); + | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option - --> $DIR/panic_in_result.rs:26:5 - | -LL | / fn option_with_unimplemented() -> Option // should emit lint -LL | | { -LL | | unimplemented!(); -LL | | } - | |_____^ - | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option -note: will cause the application to crash. - --> $DIR/panic_in_result.rs:28:9 - | -LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used unimplemented, unreachable or panic in a function that returns result or option - --> $DIR/panic_in_result.rs:31:5 - | -LL | / fn option_with_panic() -> Option // should emit lint -LL | | { -LL | | panic!("error"); -LL | | } - | |_____^ - | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option -note: will cause the application to crash. - --> $DIR/panic_in_result.rs:33:9 - | -LL | panic!("error"); - | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors From e8be047c5b2d5b1522a16b4b52cc7acfa4581ca3 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Fri, 28 Aug 2020 09:31:12 +0200 Subject: [PATCH 0517/1110] Update clippy_lints/src/utils/mod.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 07ec59f452a..d9598c4abbd 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -868,7 +868,7 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> } /// Walk into `ty` and returns `true` if any inner type is the same as `other_ty` -pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { +pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, From ffaadae8e48699f115eafd2853c252f546a69a28 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Fri, 28 Aug 2020 09:31:29 +0200 Subject: [PATCH 0518/1110] Update clippy_lints/src/utils/mod.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d9598c4abbd..82005257115 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -867,7 +867,7 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(&ret_ty) } -/// Walk into `ty` and returns `true` if any inner type is the same as `other_ty` +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), From 73b1ee1a614aaad7dd43958280ff4a444c8b737e Mon Sep 17 00:00:00 2001 From: Thibaud Date: Fri, 28 Aug 2020 09:33:05 +0200 Subject: [PATCH 0519/1110] Update clippy_lints/src/methods/mod.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/methods/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 63e0c183113..9996df69470 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1686,6 +1686,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); if item.ident.name == sym!(new); if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); From 5574182b4d2d08c848a88a1ac5633fc194e0465e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:40:22 +0900 Subject: [PATCH 0520/1110] Add a new lint to prevent `create_dir` from being used --- CHANGELOG.md | 1 + clippy_lints/src/create_dir.rs | 50 ++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 +++ src/lintlist/mod.rs | 7 +++++ tests/ui/create_dir.fixed | 13 +++++++++ tests/ui/create_dir.rs | 13 +++++++++ tests/ui/create_dir.stderr | 16 +++++++++++ 7 files changed, 104 insertions(+) create mode 100644 clippy_lints/src/create_dir.rs create mode 100644 tests/ui/create_dir.fixed create mode 100644 tests/ui/create_dir.rs create mode 100644 tests/ui/create_dir.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..b37273af44d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1444,6 +1444,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs new file mode 100644 index 00000000000..229536bd673 --- /dev/null +++ b/clippy_lints/src/create_dir.rs @@ -0,0 +1,50 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. + /// + /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// std::fs::create_dir("foo") + /// ``` + /// Use instead: + /// ```rust + /// std::fs::create_dir_all("foo") + /// ``` + pub CREATE_DIR, + restriction, + "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`" +} + +declare_lint_pass!(CreateDir => [CREATE_DIR]); + +impl LateLintPass<'_> for CreateDir { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["std", "fs", "create_dir"]); + then { + span_lint_and_sugg( + cx, + CREATE_DIR, + expr.span, + "calling `std::fs::create_dir` where there may be a better way", + "consider calling `std::fs::create_dir_all` instead", + format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + Applicability::MachineApplicable, + ) + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..7943be34c62 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod collapsible_if; mod comparison_chain; mod copies; mod copy_iterator; +mod create_dir; mod dbg_macro; mod default_trait_access; mod dereference; @@ -511,6 +512,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, + &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, @@ -1042,6 +1044,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); @@ -1104,6 +1107,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&exit::EXIT), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..e5134510922 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -290,6 +290,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copy_iterator", }, + Lint { + name: "create_dir", + group: "restriction", + desc: "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`", + deprecation: None, + module: "create_dir", + }, Lint { name: "crosspointer_transmute", group: "complexity", diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed new file mode 100644 index 00000000000..50f31f0c9c5 --- /dev/null +++ b/tests/ui/create_dir.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir_all("foo"); + std::fs::create_dir_all("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs new file mode 100644 index 00000000000..00ef37f413f --- /dev/null +++ b/tests/ui/create_dir.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir("foo"); + std::fs::create_dir("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr new file mode 100644 index 00000000000..3ae4680d929 --- /dev/null +++ b/tests/ui/create_dir.stderr @@ -0,0 +1,16 @@ +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:8:5 + | +LL | std::fs::create_dir("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | + = note: `-D clippy::create-dir` implied by `-D warnings` + +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:9:5 + | +LL | std::fs::create_dir("bar").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + +error: aborting due to 2 previous errors + From 607905d126c55422668007737c22d7a7a89c0d57 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:53:15 +0900 Subject: [PATCH 0521/1110] Add STD_FS_CREATE_DIR into paths --- clippy_lints/src/create_dir.rs | 4 ++-- clippy_lints/src/utils/paths.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 229536bd673..7ba6facda6a 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::*; @@ -33,7 +33,7 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["std", "fs", "create_dir"]); + if match_qpath(path, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index d44854aefe9..65320d6a0e0 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -110,6 +110,7 @@ pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; From 34e302e67c08c9b97d58d062ea83cc1fd860c56e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 19:35:04 +0900 Subject: [PATCH 0522/1110] Fix clippy error --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 7ba6facda6a..4fede8857c7 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,7 +1,7 @@ use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; From eebd2483654456e332d7cf53218b56b9cbd6f2f5 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 19:56:03 +0900 Subject: [PATCH 0523/1110] Fix errors --- clippy_lints/src/create_dir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 4fede8857c7..bf47c1f84a2 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -15,11 +15,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// std::fs::create_dir("foo") + /// std::fs::create_dir("foo"); /// ``` /// Use instead: /// ```rust - /// std::fs::create_dir_all("foo") + /// std::fs::create_dir_all("foo"); /// ``` pub CREATE_DIR, restriction, From 7a66e6502dc3c7085b3f4078c01d4957d96175ed Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 29 Aug 2020 01:18:42 +0200 Subject: [PATCH 0524/1110] or_fn_call: ignore nullary associated const fns --- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/or_fun_call.fixed | 17 +++++++++++------ tests/ui/or_fun_call.rs | 17 +++++++++++------ tests/ui/or_fun_call.stderr | 20 ++++---------------- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 82005257115..fe2ee093157 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -899,7 +899,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..5fb568672d3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); - let mut map = HashMap::::new(); - map.entry(42).or_insert_with(String::new); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert_with(String::new); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..737b0f7e55b 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..b8a436993f3 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,35 +60,23 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 - | -LL | map.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 - | -LL | btree.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:62:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:87:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:91:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors From 5b7590f841974255f74c64d573189aecc7a30b2e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 29 Aug 2020 14:20:01 +0900 Subject: [PATCH 0525/1110] Downgrade applicability of `create_dir` --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index bf47c1f84a2..1042eb45524 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) } } From 4972989b616cbf96c015cd9fdf1f4b4464ecaace Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 14 Aug 2020 17:30:48 -0700 Subject: [PATCH 0526/1110] Add a lint for an async block/closure that yields a type that is itself awaitable. This catches bugs of the form tokio::spawn(async move { let f = some_async_thing(); f // Oh no I forgot to await f so that work will never complete. }); --- CHANGELOG.md | 1 + clippy_lints/src/async_yields_async.rs | 88 +++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ src/lintlist/mod.rs | 7 ++ tests/ui/async_yields_async.fixed | 61 ++++++++++++++++ tests/ui/async_yields_async.rs | 61 ++++++++++++++++ tests/ui/async_yields_async.stderr | 96 ++++++++++++++++++++++++++ 7 files changed, 319 insertions(+) create mode 100644 clippy_lints/src/async_yields_async.rs create mode 100644 tests/ui/async_yields_async.fixed create mode 100644 tests/ui/async_yields_async.rs create mode 100644 tests/ui/async_yields_async.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d48821023..99a8b1a6293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1512,6 +1512,7 @@ Released 2018-09-13 [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs new file mode 100644 index 00000000000..ae347fcd3e8 --- /dev/null +++ b/clippy_lints/src/async_yields_async.rs @@ -0,0 +1,88 @@ +use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for async blocks that yield values of types that can themselves + /// be awaited. + /// + /// **Why is this bad?** + /// An await is likely missing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo() + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo().await + /// }; + /// } + /// ``` + pub ASYNC_YIELDS_ASYNC, + correctness, + "async blocks that return a type that can be awaited" +} + +declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); + +impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + use AsyncGeneratorKind::{Block, Closure}; + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind { + if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + let expr_ty = typeck_results.expr_ty(&body.value); + + if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { + let return_expr_span = match &body.value.kind { + // XXXkhuey there has to be a better way. + ExprKind::Block(block, _) => block.expr.map(|e| e.span), + ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), + _ => None, + }; + if let Some(return_expr_span) = return_expr_span { + span_lint_and_then( + cx, + ASYNC_YIELDS_ASYNC, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body.value.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..0eb1d331366 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -154,6 +154,7 @@ mod arithmetic; mod as_conversions; mod assertions_on_constants; mod assign_ops; +mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; @@ -483,6 +484,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, + &async_yields_async::ASYNC_YIELDS_ASYNC, &atomic_ordering::INVALID_ATOMIC_ORDERING, &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, &attrs::DEPRECATED_CFG_ATTR, @@ -1099,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1232,6 +1235,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_CFG_ATTR), @@ -1675,6 +1679,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..dff19ef440f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -52,6 +52,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "assign_ops", }, + Lint { + name: "async_yields_async", + group: "correctness", + desc: "async blocks that return a type that can be awaited", + deprecation: None, + module: "async_yields_async", + }, Lint { name: "await_holding_lock", group: "pedantic", diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed new file mode 100644 index 00000000000..cadc6494c76 --- /dev/null +++ b/tests/ui/async_yields_async.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + }.await + }; + let _i = async { + CustomFutureType.await + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + }.await + }; + let _k = async || { + CustomFutureType.await + }; + let _l = async || CustomFutureType.await; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType.await + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs new file mode 100644 index 00000000000..898fe1a9561 --- /dev/null +++ b/tests/ui/async_yields_async.rs @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + } + }; + let _i = async { + CustomFutureType + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + } + }; + let _k = async || { + CustomFutureType + }; + let _l = async || CustomFutureType; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr new file mode 100644 index 00000000000..112984cdccb --- /dev/null +++ b/tests/ui/async_yields_async.stderr @@ -0,0 +1,96 @@ +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:34:9 + | +LL | let _h = async { + | ____________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | + = note: `-D clippy::async-yields-async` implied by `-D warnings` +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:39:9 + | +LL | let _i = async { + | ____________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:45:9 + | +LL | let _j = async || { + | _______________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:50:9 + | +LL | let _k = async || { + | _______________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:52:23 + | +LL | let _l = async || CustomFutureType; + | ^^^^^^^^^^^^^^^^ + | | + | outer async construct + | awaitable value not awaited + | help: consider awaiting this value: `CustomFutureType.await` + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:58:9 + | +LL | let _m = async || { + | _______________________- +LL | | println!("I'm bored"); +LL | | // Some more stuff +LL | | +LL | | // Finally something to await +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: aborting due to 6 previous errors + From c1d2b9376a6bb4fc06f845e12b9c2a93079bb2ee Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 22 Aug 2020 21:36:39 -0700 Subject: [PATCH 0527/1110] Add a test for an async function. --- tests/ui/async_yields_async.fixed | 7 +++++++ tests/ui/async_yields_async.rs | 7 +++++++ tests/ui/async_yields_async.stderr | 12 ++++++------ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index cadc6494c76..9b1a7ac3ba9 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -22,6 +22,12 @@ fn custom_future_type_ctor() -> CustomFutureType { CustomFutureType } +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + #[rustfmt::skip] fn main() { let _f = { @@ -58,4 +64,5 @@ fn main() { CustomFutureType.await }; let _n = async || custom_future_type_ctor(); + let _o = async || f(); } diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index 898fe1a9561..731c094edb4 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -22,6 +22,12 @@ fn custom_future_type_ctor() -> CustomFutureType { CustomFutureType } +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + #[rustfmt::skip] fn main() { let _f = { @@ -58,4 +64,5 @@ fn main() { CustomFutureType }; let _n = async || custom_future_type_ctor(); + let _o = async || f(); } diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 112984cdccb..17d0c375106 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -1,5 +1,5 @@ error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:34:9 + --> $DIR/async_yields_async.rs:40:9 | LL | let _h = async { | ____________________- @@ -20,7 +20,7 @@ LL | }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:39:9 + --> $DIR/async_yields_async.rs:45:9 | LL | let _i = async { | ____________________- @@ -33,7 +33,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:45:9 + --> $DIR/async_yields_async.rs:51:9 | LL | let _j = async || { | _______________________- @@ -53,7 +53,7 @@ LL | }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:50:9 + --> $DIR/async_yields_async.rs:56:9 | LL | let _k = async || { | _______________________- @@ -66,7 +66,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:52:23 + --> $DIR/async_yields_async.rs:58:23 | LL | let _l = async || CustomFutureType; | ^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _l = async || CustomFutureType; | help: consider awaiting this value: `CustomFutureType.await` error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:58:9 + --> $DIR/async_yields_async.rs:64:9 | LL | let _m = async || { | _______________________- From 04912ca115ff153a97d80b604435b10dcb155dd0 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 22 Aug 2020 21:40:01 -0700 Subject: [PATCH 0528/1110] Formatting changes requested by ThibsG. --- clippy_lints/src/async_yields_async.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index ae347fcd3e8..88d9d3b5a26 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -5,12 +5,10 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** - /// Checks for async blocks that yield values of types that can themselves - /// be awaited. + /// **What it does:** Checks for async blocks that yield values of types + /// that can themselves be awaited. /// - /// **Why is this bad?** - /// An await is likely missing. + /// **Why is this bad?** An await is likely missing. /// /// **Known problems:** None. /// From a424a2c1676a29c147252873037e8943d54941d3 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Sat, 29 Aug 2020 16:17:53 -0700 Subject: [PATCH 0529/1110] changed check_impl_item to check_fn and added a few more test cases --- clippy_lints/src/panic_in_result.rs | 69 ++++++++++++++--------------- tests/ui/panic_in_result.rs | 20 ++++++++- tests/ui/panic_in_result.stderr | 60 +++++++++++++++++++------ 3 files changed, 99 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 2901f393fc6..11fefc12316 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -1,6 +1,8 @@ use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; +use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -21,7 +23,6 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` - pub PANIC_IN_RESULT, restriction, "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " @@ -30,22 +31,26 @@ declare_clippy_lint! { declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for PanicInResult { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + /* + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + span: Span, + hir_id: hir::HirId, + ) { if_chain! { - // first check if it's a method or function - if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; - // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)); - then { - lint_impl_body(cx, impl_item.span, impl_item); + if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); + then + { + lint_impl_body(cx, span, body); } } - } + }*/ } -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; - struct FindPanicUnimplementedUnreachable { result: Vec, } @@ -70,29 +75,21 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if_chain! { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - then { - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindPanicUnimplementedUnreachable { - result: Vec::new(), - }; - fpu.visit_expr(&body.value); - - // if we've found one, lint - if !fpu.result.is_empty() { - span_lint_and_then( - cx, - PANIC_IN_RESULT, - impl_span, - "used unimplemented, unreachable, todo or panic in a function that returns result", - move |diag| { - diag.help( - "unimplemented, unreachable, todo or panic should not be used in a function that returns result" ); - diag.span_note(fpu.result, "will cause the application to crash."); - }); - } - } +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { + let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; + panics.visit_expr(&body.value); + if !panics.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT, + impl_span, + "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + move |diag| { + diag.help( + "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + ); + diag.span_note(panics.result, "return Err() instead of panicking"); + }, + ); } } diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs index 056778995a4..f6fb2f1ab61 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result.rs @@ -49,4 +49,22 @@ impl A { } } -fn main() {} +fn function_result_with_panic() -> Result // should emit lint +{ + panic!("error"); +} + +fn todo() { + println!("something"); +} + +fn function_result_with_custom_todo() -> Result // should not emit lint +{ + todo(); + Ok(true) +} + +fn main() -> Result<(), String> { + todo!("finish main method"); + Ok(()) +} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr index 3b9ac69f20d..9faedf82986 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result.stderr @@ -1,4 +1,4 @@ -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,15 +8,15 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:8:9 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,15 +25,15 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:13:9 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,15 +42,15 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:18:9 | LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:21:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,13 +59,47 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:23:9 | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result.rs:52:1 + | +LL | / fn function_result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result.rs:54:5 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result.rs:67:1 + | +LL | / fn main() -> Result<(), String> { +LL | | todo!("finish main method"); +LL | | Ok(()) +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result.rs:68:5 + | +LL | todo!("finish main method"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors From 73a3288282e733bfc5893e9920d29f1de5a21591 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Sat, 29 Aug 2020 16:22:15 -0700 Subject: [PATCH 0530/1110] uncommented fn --- clippy_lints/src/panic_in_result.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 11fefc12316..8ba365cb00c 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -31,7 +31,6 @@ declare_clippy_lint! { declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for PanicInResult { - /* fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -48,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { lint_impl_body(cx, span, body); } } - }*/ + } } struct FindPanicUnimplementedUnreachable { From 17b2ba5ded12f59dba63ece659b5cd714b763800 Mon Sep 17 00:00:00 2001 From: Camelid <37223377+camelid@users.noreply.github.com> Date: Sun, 30 Aug 2020 11:24:15 -0700 Subject: [PATCH 0531/1110] Syntax-highlight `single_char_push_str` lint --- clippy_lints/src/methods/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9996df69470..7c4a78cbdcd 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1324,20 +1324,20 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using push_str with a single-character string literal, - /// and push with a char would work fine. + /// **What it does:** Warns when using `push_str` with a single-character string literal, + /// and `push` with a `char` would work fine. /// - /// **Why is this bad?** It's less clear that we are pushing a single character + /// **Why is this bad?** It's less clear that we are pushing a single character. /// /// **Known problems:** None /// /// **Example:** - /// ``` + /// ```rust /// let mut string = String::new(); /// string.push_str("R"); /// ``` /// Could be written as - /// ``` + /// ```rust /// let mut string = String::new(); /// string.push('R'); /// ``` From 451ef7880392f3f06088ff7a7b957e3485f4bc6c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 31 Aug 2020 22:40:47 +0900 Subject: [PATCH 0532/1110] Use `match_def_path` instead of `match_qpath` --- clippy_lints/src/create_dir.rs | 5 +++-- tests/ui/create_dir.fixed | 6 ++++-- tests/ui/create_dir.rs | 6 ++++-- tests/ui/create_dir.stderr | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 1042eb45524..f80a0efa7a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -33,7 +33,8 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &paths::STD_FS_CREATE_DIR); + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 50f31f0c9c5..0e28f87e33d 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir_all("foo"); std::fs::create_dir_all("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 00ef37f413f..1f226298c0d 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir("foo"); std::fs::create_dir("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 3ae4680d929..0c97bdd0f7a 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,5 +1,5 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:8:5 + --> $DIR/create_dir.rs:9:5 | LL | std::fs::create_dir("foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` @@ -7,7 +7,7 @@ LL | std::fs::create_dir("foo"); = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:10:5 | LL | std::fs::create_dir("bar").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` From 001f9e45f24c5617d816e7d9dfbca4dc1a694dd9 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 00:05:53 +0900 Subject: [PATCH 0533/1110] Fix the wrong suggestion when using macro in `collapsible_if` --- clippy_lints/src/utils/sugg.rs | 6 +++++- tests/ui/collapsible_if.fixed | 3 +++ tests/ui/collapsible_if.rs | 5 +++++ tests/ui/collapsible_if.stderr | 10 +++++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 2955f8d8e59..811fde388d1 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -132,7 +132,11 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let snippet = snippet(cx, expr.span, default); + let snippet = if expr.span.from_expansion() { + snippet_with_macro_callsite(cx, expr.span, default) + } else { + snippet(cx, expr.span, default) + }; match expr.kind { ast::ExprKind::AddrOf(..) diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 561283fc8e7..efd4187947b 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -135,4 +135,7 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) && matches!(true, true) {} } diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index dc9d9b451c0..657f32d38a3 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -149,4 +149,9 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) { + if matches!(true, true) {} + } } diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index f56dd65b9dd..acd1ec3f2ca 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -118,5 +118,13 @@ LL | println!("Hello world!"); LL | } | -error: aborting due to 7 previous errors +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:154:5 + | +LL | / if matches!(true, true) { +LL | | if matches!(true, true) {} +LL | | } + | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + +error: aborting due to 8 previous errors From 8b0aa6a00b19b8e47a72157ec8e8f9e9060cb2fb Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 00:31:53 +0900 Subject: [PATCH 0534/1110] default_trait_access: Fix wrong suggestion --- clippy_lints/src/default_trait_access.rs | 4 +- tests/ui/default_trait_access.fixed | 106 +++++++++++++++++++++++ tests/ui/default_trait_access.rs | 5 +- tests/ui/default_trait_access.stderr | 26 +++--- 4 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 tests/ui/default_trait_access.fixed diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 067ea903bdd..0b0a1307876 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(..) = expr_ty.kind { - let replacement = format!("{}::default()", expr_ty); + if let ty::Adt(def, ..) = expr_ty.kind { + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed new file mode 100644 index 00000000000..d05567a3f82 --- /dev/null +++ b/tests/ui/default_trait_access.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = std::string::String::default(); + + let s2 = String::default(); + + let s3: String = std::string::String::default(); + + let s4: String = std::string::String::default(); + + let s5 = string::String::default(); + + let s6: String = std::string::String::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault = GenericDerivedDefault::default(); + + let s12 = GenericDerivedDefault::::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = TupleDerivedDefault::default(); + + let s15: ArrayDerivedDefault = ArrayDerivedDefault::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = ::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly() -> T { + Default::default() + } + + pub fn make_t_nicely() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 2f1490a7036..447e70c0bbb 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,4 +1,7 @@ -#![warn(clippy::default_trait_access)] +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] use std::default; use std::default::Default as D2; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 26b2057548b..df8a5b94ddc 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,49 +1,53 @@ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:8:22 + --> $DIR/default_trait_access.rs:11:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | - = note: `-D clippy::default-trait-access` implied by `-D warnings` +note: the lint level is defined here + --> $DIR/default_trait_access.rs:4:9 + | +LL | #![deny(clippy::default_trait_access)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:12:22 + --> $DIR/default_trait_access.rs:15:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:14:22 + --> $DIR/default_trait_access.rs:17:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:18:22 + --> $DIR/default_trait_access.rs:21:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:28:46 +error: calling `GenericDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:31:46 | LL | let s11: GenericDerivedDefault = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:34:36 + --> $DIR/default_trait_access.rs:37:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:36:36 + --> $DIR/default_trait_access.rs:39:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:40:42 + --> $DIR/default_trait_access.rs:43:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` From f9fcbbea03edb735c22311522b55d7b854bd6ac0 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Mon, 31 Aug 2020 13:32:05 -0700 Subject: [PATCH 0535/1110] fixed bug --- clippy_lints/src/panic_in_result.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 8ba365cb00c..8b8e211cb72 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -34,12 +34,15 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: FnKind<'tcx>, + fn_kind: FnKind<'tcx>, _: &'tcx hir::FnDecl<'tcx>, body: &'tcx hir::Body<'tcx>, span: Span, hir_id: hir::HirId, ) { + if let FnKind::Closure(_) = fn_kind { + return; + } if_chain! { if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); then From afeb917fca7eaa5b44acd29a60f687024c0ebeac Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 1 Sep 2020 11:21:48 +1200 Subject: [PATCH 0536/1110] Fix a fp in `transmute_ptr_to_ptr` Avoid firing the lint when `transmute` in const contexts as dereferencing raw pointers in consts is unstable. cc #5959 --- clippy_lints/src/transmute.rs | 6 ++++-- tests/ui/transmute_ptr_to_ptr.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 50d9c93f9d4..789d124eae2 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,8 +331,9 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting from/to bits in const contexts. + // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + // 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(&args[0]); @@ -486,7 +487,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { Applicability::Unspecified, ); } else { - if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + if (cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty)) + && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 0d8a322f2b2..26b03bdc740 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -51,4 +51,12 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; } +// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) +const _: &() = { + struct ZST; + let zst = &ZST; + + unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } +}; + fn main() {} From 2e4b4cebbbb37efa5dc69dd2616f3b7a288b92aa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 12:09:32 +0900 Subject: [PATCH 0537/1110] useless_attribute: Permit wildcard_imports and enum_glob_use --- clippy_lints/src/attrs.rs | 38 ++++++++++++++++++------------- tests/ui/useless_attribute.fixed | 8 +++++++ tests/ui/useless_attribute.rs | 8 +++++++ tests/ui/useless_attribute.stderr | 2 +- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index cfcc1b3c5f3..c8f153e7201 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -71,8 +71,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `extern crate` and `use` items annotated with /// lint attributes. /// - /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]` and - /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on + /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, + /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and + /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on /// `extern crate` items with a `#[macro_use]` attribute. /// /// **Why is this bad?** Lint attributes have no effect on crate imports. Most @@ -318,7 +319,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { if let Some(ident) = attr.ident() { match &*ident.as_str() { "allow" | "warn" | "deny" | "forbid" => { - // permit `unused_imports`, `deprecated` and `unreachable_pub` for `use` items + // permit `unused_imports`, `deprecated`, `unreachable_pub`, + // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { @@ -327,6 +329,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym!(deprecated)) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) + || extract_clippy_lint(lint) + .map_or(false, |s| s == "wildcard_imports") + || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") { return; } @@ -387,24 +392,25 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { - fn extract_name(lint: &NestedMetaItem) -> Option { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - then { - return Some(lint_name.as_str()); - } +/// Returns the lint name if it is clippy lint. +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { + if_chain! { + if let Some(meta_item) = lint.meta_item(); + if meta_item.path.segments.len() > 1; + if let tool_name = meta_item.path.segments[0].ident; + if tool_name.as_str() == "clippy"; + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + then { + return Some(lint_name.as_str()); } - None } + None +} +fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { let lint_store = cx.lints(); for lint in items { - if let Some(lint_name) = extract_name(lint) { + if let Some(lint_name) = extract_clippy_lint(lint) { if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) { diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index b222e2f7976..a5fcde768f1 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 3422eace4ab..0396d39e3d5 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 57ba976730c..d0194e4bbbe 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:53:5 + --> $DIR/useless_attribute.rs:61:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` From b30422114e6bb7235398f21fe13ffa09429b7d0f Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Tue, 1 Sep 2020 14:05:19 +0100 Subject: [PATCH 0538/1110] Allow GraphQL in doc without backticks --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 292dbd7ad6b..9c5a12ea9c8 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -122,7 +122,7 @@ define_Conf! { "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", - "OAuth", + "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "TensorFlow", From aa7ffa5257667edb284de16b529df3d4111d70ab Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 22:39:09 +0900 Subject: [PATCH 0539/1110] Fix FP in `same_item_push` Don't emit a lint when the pushed item doesn't have Clone trait --- clippy_lints/src/loops.rs | 78 +++++++++++++++++++++----------------- tests/ui/same_item_push.rs | 6 +++ 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c95e43a9430..25345f8fa31 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,43 +1140,51 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + let ty = cx.typeck_results().expr_ty(pushed_item); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) } } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bfe27e02044..0903a873826 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -94,4 +94,10 @@ fn main() { vec13.push(item); item += 10; } + + // Fix #5979 + let mut vec14: Vec = Vec::new(); + for _ in 0..10 { + vec14.push(std::fs::File::open("foobar").unwrap()); + } } From e49a29933be3bd988ccb75b053f480d9c99a7ff5 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 16:26:59 -0400 Subject: [PATCH 0540/1110] Working map_err_ignore lint --- clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/map_err_ignore.rs | 108 +++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 clippy_lints/src/map_err_ignore.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0eb1d331366..8e80779377b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod map_clone; +mod map_err_ignore; mod map_identity; mod map_unit_fn; mod match_on_vec_items; @@ -624,6 +625,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, + &map_err_ignore::MAP_ERR_IGNORE, &map_identity::MAP_IDENTITY, &map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN, @@ -916,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box methods::Methods); store.register_late_pass(|| box map_clone::MapClone); + store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); store.register_late_pass(|| box types::LetUnitValue); store.register_late_pass(|| box types::UnitCmp); @@ -1327,6 +1330,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_err_ignore::MAP_ERR_IGNORE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1534,6 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs new file mode 100644 index 00000000000..c63c201a9f3 --- /dev/null +++ b/clippy_lints/src/map_err_ignore.rs @@ -0,0 +1,108 @@ +use crate::utils::span_lint_and_sugg; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, CaptureBy, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` + /// + /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to bubble the original error + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// enum Errors { + /// Ignore + ///} + ///fn main() -> Result<(), Errors> { + /// + /// let x = u32::try_from(-123_i32); + /// + /// println!("{:?}", x.map_err(|_| Errors::Ignore)); + /// + /// Ok(()) + ///} + /// ``` + /// Use instead: + /// ```rust + /// enum Errors { + /// WithContext(TryFromIntError) + ///} + ///fn main() -> Result<(), Errors> { + /// + /// let x = u32::try_from(-123_i32); + /// + /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); + /// + /// Ok(()) + ///} + /// ``` + pub MAP_ERR_IGNORE, + style, + "`map_err` should not ignore the original error" +} + +declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]); + +impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { + // do not try to lint if this is from a macro or desugaring + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if e.span.from_expansion() { + return; + } + + // check if this is a method call (e.g. x.foo()) + if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { + // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] Enum::Variant[2])) + if method.ident.as_str() == "map_err" && args.len() == 2 { + // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields + if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { + // check if this is by Reference (meaning there's no move statement) + if capture == CaptureBy::Ref { + // Get the closure body to check the parameters and values + let closure_body = cx.tcx.hir().body(body_id); + // make sure there's only one parameter (`|_|`) + if closure_body.params.len() == 1 { + // make sure that parameter is the wild token (`_`) + if let PatKind::Wild = closure_body.params[0].pat.kind { + // Check the value of the closure to see if we can build the enum we are throwing away the error for + // make sure this is a Path + if let ExprKind::Path(q_path) = &closure_body.value.kind { + // this should be a resolved path, only keep the path field + if let QPath::Resolved(_, path) = q_path { + // finally get the idents for each path segment collect them as a string and join them with the path separator ("::"") + let closure_fold: String = path.segments.iter().map(|x| x.ident.as_str().to_string()).collect::>().join("::"); + //Span the body of the closure (the |...| bit) and suggest the fix by taking the error and encapsulating it in the enum + span_lint_and_sugg( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_err` has thrown away the original error", + "Allow the error enum to encapsulate the original error", + format!("|e| {}(e)", closure_fold), + Applicability::HasPlaceholders, + ); + } + } else { + //If we cannot build the enum in a human readable way just suggest not throwing way the error + span_lint_and_sugg( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_err` has thrown away the original error", + "Allow the error enum to encapsulate the original error", + "|e|".to_string(), + Applicability::HasPlaceholders, + ); + } + } + } + } + } + } + } + } +} \ No newline at end of file From 202a80c927412a548180eca5990ee764d76ed643 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 16:59:37 -0400 Subject: [PATCH 0541/1110] Added tests for map_err, ignored map_err lint on drop_ref tests --- clippy_lints/src/map_err_ignore.rs | 49 ++++++++++++++++++------------ tests/ui/drop_ref.rs | 1 + tests/ui/drop_ref.stderr | 36 +++++++++++----------- tests/ui/map_err.rs | 24 +++++++++++++++ tests/ui/map_err.stderr | 10 ++++++ 5 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 tests/ui/map_err.rs create mode 100644 tests/ui/map_err.stderr diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index c63c201a9f3..43bfcf0b8f1 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,6 +1,6 @@ use crate::utils::span_lint_and_sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, CaptureBy, PatKind, QPath}; +use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -18,7 +18,7 @@ declare_clippy_lint! { /// Ignore ///} ///fn main() -> Result<(), Errors> { - /// + /// /// let x = u32::try_from(-123_i32); /// /// println!("{:?}", x.map_err(|_| Errors::Ignore)); @@ -32,7 +32,7 @@ declare_clippy_lint! { /// WithContext(TryFromIntError) ///} ///fn main() -> Result<(), Errors> { - /// + /// /// let x = u32::try_from(-123_i32); /// /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]); impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { - // do not try to lint if this is from a macro or desugaring + // do not try to lint if this is from a macro or desugaring fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if e.span.from_expansion() { return; @@ -56,26 +56,34 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { // check if this is a method call (e.g. x.foo()) if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { - // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] Enum::Variant[2])) + // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] + // Enum::Variant[2])) if method.ident.as_str() == "map_err" && args.len() == 2 { // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields - if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { + if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { // check if this is by Reference (meaning there's no move statement) - if capture == CaptureBy::Ref { - // Get the closure body to check the parameters and values + if capture == CaptureBy::Ref { + // Get the closure body to check the parameters and values let closure_body = cx.tcx.hir().body(body_id); // make sure there's only one parameter (`|_|`) - if closure_body.params.len() == 1 { - // make sure that parameter is the wild token (`_`) + if closure_body.params.len() == 1 { + // make sure that parameter is the wild token (`_`) if let PatKind::Wild = closure_body.params[0].pat.kind { - // Check the value of the closure to see if we can build the enum we are throwing away the error for - // make sure this is a Path + // Check the value of the closure to see if we can build the enum we are throwing away + // the error for make sure this is a Path if let ExprKind::Path(q_path) = &closure_body.value.kind { // this should be a resolved path, only keep the path field if let QPath::Resolved(_, path) = q_path { - // finally get the idents for each path segment collect them as a string and join them with the path separator ("::"") - let closure_fold: String = path.segments.iter().map(|x| x.ident.as_str().to_string()).collect::>().join("::"); - //Span the body of the closure (the |...| bit) and suggest the fix by taking the error and encapsulating it in the enum + // finally get the idents for each path segment collect them as a string and + // join them with the path separator ("::"") + let closure_fold: String = path + .segments + .iter() + .map(|x| x.ident.as_str().to_string()) + .collect::>() + .join("::"); + //Span the body of the closure (the |...| bit) and suggest the fix by taking + // the error and encapsulating it in the enum span_lint_and_sugg( cx, MAP_ERR_IGNORE, @@ -84,10 +92,11 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { "Allow the error enum to encapsulate the original error", format!("|e| {}(e)", closure_fold), Applicability::HasPlaceholders, - ); + ); } } else { - //If we cannot build the enum in a human readable way just suggest not throwing way the error + //If we cannot build the enum in a human readable way just suggest not throwing way + // the error span_lint_and_sugg( cx, MAP_ERR_IGNORE, @@ -96,13 +105,13 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { "Allow the error enum to encapsulate the original error", "|e|".to_string(), Applicability::HasPlaceholders, - ); + ); } } } - } + } } } } } -} \ No newline at end of file +} diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 9181d789d4f..6b5bcdaa78e 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,5 +1,6 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] +#![allow(clippy::map_err_ignore)] use std::mem::drop; diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 35ae88b78a4..7974bf56d44 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:9:5 + --> $DIR/drop_ref.rs:10:5 | LL | drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::drop-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:9:10 + --> $DIR/drop_ref.rs:10:10 | LL | drop(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:12:5 + --> $DIR/drop_ref.rs:13:5 | LL | drop(&owned1); | ^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:12:10 + --> $DIR/drop_ref.rs:13:10 | LL | drop(&owned1); | ^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:13:5 + --> $DIR/drop_ref.rs:14:5 | LL | drop(&&owned1); | ^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/drop_ref.rs:13:10 + --> $DIR/drop_ref.rs:14:10 | LL | drop(&&owned1); | ^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:14:5 + --> $DIR/drop_ref.rs:15:5 | LL | drop(&mut owned1); | ^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:14:10 + --> $DIR/drop_ref.rs:15:10 | LL | drop(&mut owned1); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:18:5 + --> $DIR/drop_ref.rs:19:5 | LL | drop(reference1); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:18:10 + --> $DIR/drop_ref.rs:19:10 | LL | drop(reference1); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:21:5 + --> $DIR/drop_ref.rs:22:5 | LL | drop(reference2); | ^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:21:10 + --> $DIR/drop_ref.rs:22:10 | LL | drop(reference2); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:24:5 + --> $DIR/drop_ref.rs:25:5 | LL | drop(reference3); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:24:10 + --> $DIR/drop_ref.rs:25:10 | LL | drop(reference3); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:29:5 + --> $DIR/drop_ref.rs:30:5 | LL | drop(&val); | ^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/drop_ref.rs:29:10 + --> $DIR/drop_ref.rs:30:10 | LL | drop(&val); | ^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:37:5 + --> $DIR/drop_ref.rs:38:5 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:37:20 + --> $DIR/drop_ref.rs:38:20 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs new file mode 100644 index 00000000000..f3a74ad95cd --- /dev/null +++ b/tests/ui/map_err.rs @@ -0,0 +1,24 @@ +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +enum Errors { + Ignored, +} + +impl Error for Errors {} + +impl fmt::Display for Errors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +fn main() -> Result<(), Errors> { + let x = u32::try_from(-123_i32); + + println!("{:?}", x.map_err(|_| Errors::Ignored)); + + Ok(()) +} diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr new file mode 100644 index 00000000000..8cd37d8c025 --- /dev/null +++ b/tests/ui/map_err.stderr @@ -0,0 +1,10 @@ +error: `map_err` has thrown away the original error + --> $DIR/map_err.rs:21:32 + | +LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); + | ^^^ help: Allow the error enum to encapsulate the original error: `|e| Errors::Ignored(e)` + | + = note: `-D clippy::map-err-ignore` implied by `-D warnings` + +error: aborting due to previous error + From 337729137bdec31b55665653ef0cf7dc124b0eaa Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 17:05:40 -0400 Subject: [PATCH 0542/1110] Ran cargo dev update_lints --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 +- src/lintlist/mod.rs | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a8b1a6293..dfe45a46f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1675,6 +1675,7 @@ Released 2018-09-13 [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten [`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8e80779377b..3794cae091a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1538,7 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_err_ignore::MAP_ERR_IGNORE), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index dff19ef440f..6725c97f793 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1165,6 +1165,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "entry", }, + Lint { + name: "map_err_ignore", + group: "style", + desc: "`map_err` should not ignore the original error", + deprecation: None, + module: "map_err_ignore", + }, Lint { name: "map_flatten", group: "pedantic", From a5754a1fad6eeb765bc825dcc657134985576787 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 2 Sep 2020 08:57:00 +0200 Subject: [PATCH 0543/1110] Run cargo dev fmt --- clippy_lints/src/utils/ast_utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 3c3f8b26e3a..fa8dd210eba 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -191,7 +191,9 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r), (Empty, Empty) => true, - (MacCall(l), MacCall(r)) => l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)), + (MacCall(l), MacCall(r)) => { + l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + }, _ => false, } } From 22c994061359aa9e0e08e44cd698dbfc6553834c Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Wed, 2 Sep 2020 12:51:44 +0100 Subject: [PATCH 0544/1110] Add tests for allowed non-backticked identifiers in doc --- tests/ui/doc.rs | 13 +++++++++--- tests/ui/doc.stderr | 50 +++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index 77620c857e6..68c5d32846f 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -49,6 +49,16 @@ fn test_emphasis() { fn test_units() { } +/// This tests allowed identifiers. +/// DirectX +/// ECMAScript +/// OAuth GraphQL +/// TeX LaTeX BibTeX BibLaTeX +/// CamelCase (see also #2395) +/// be_sure_we_got_to_the_end_of_it +fn test_allowed() { +} + /// This test has [a link_with_underscores][chunked-example] inside it. See #823. /// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) /// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. @@ -168,9 +178,6 @@ fn issue_1920() {} /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels fn issue_1832() {} -/// Ok: CamelCase (It should not be surrounded by backticks) -fn issue_2395() {} - /// An iterator over mycrate::Collection's values. /// It should not lint a `'static` lifetime in ticks. fn issue_2210() {} diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr index ae9bb394cb9..23fca43590b 100644 --- a/tests/ui/doc.stderr +++ b/tests/ui/doc.stderr @@ -54,131 +54,137 @@ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the doc LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:58:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:52:22 + --> $DIR/doc.rs:62:22 | LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. | ^^^^^^^^^^^^^^^^^^^^^ error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:55:21 + --> $DIR/doc.rs:65:21 | LL | /// It can also be [inline_link2]. | ^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:65:5 + --> $DIR/doc.rs:75:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:73:8 + --> $DIR/doc.rs:83:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:76:7 + --> $DIR/doc.rs:86:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:78:22 + --> $DIR/doc.rs:88:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:79:5 + --> $DIR/doc.rs:89:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:86:5 + --> $DIR/doc.rs:96:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:99:5 + --> $DIR/doc.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:110:43 + --> $DIR/doc.rs:120:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:115:5 + --> $DIR/doc.rs:125:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:116:1 + --> $DIR/doc.rs:126:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:121:43 + --> $DIR/doc.rs:131:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:126:5 + --> $DIR/doc.rs:136:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:127:1 + --> $DIR/doc.rs:137:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:138:5 + --> $DIR/doc.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:165:13 + --> $DIR/doc.rs:175:13 | LL | /// Not ok: http://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:166:13 + --> $DIR/doc.rs:176:13 | LL | /// Not ok: https://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:167:13 + --> $DIR/doc.rs:177:13 | LL | /// Not ok: http://www.unicode.org/ | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:168:13 + --> $DIR/doc.rs:178:13 | LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:174:22 + --> $DIR/doc.rs:181:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors From b220ddf146f4c11011b2e1b7f37ecb8e5485555b Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 2 Sep 2020 23:30:40 +0200 Subject: [PATCH 0545/1110] unit-arg - pr remarks --- clippy_lints/src/types.rs | 82 ++++++++++++++++++------------ clippy_lints/src/utils/mod.rs | 96 ++++++++++++++++++++--------------- 2 files changed, 103 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 16e48d91916..e83d491fac3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -31,8 +31,8 @@ use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, + qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -802,7 +802,45 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } -#[allow(clippy::too_many_lines)] +fn fmt_stmts_and_call( + cx: &LateContext<'_>, + call_expr: &Expr<'_>, + call_snippet: &str, + args_snippets: &[impl AsRef], + non_empty_block_args_snippets: &[impl AsRef], +) -> String { + let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); + let call_snippet_with_replacements = args_snippets + .iter() + .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1)); + + let mut stmts_and_call = non_empty_block_args_snippets + .iter() + .map(|it| it.as_ref().to_owned()) + .collect::>(); + stmts_and_call.push(call_snippet_with_replacements); + stmts_and_call = stmts_and_call + .into_iter() + .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned()) + .collect(); + + let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); + // expr is not in a block statement or result expression position, wrap in a block + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let block_indent = call_expr_indent + 4; + stmts_and_call_snippet = + reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); + stmts_and_call_snippet = format!( + "{{\n{}{}\n{}}}", + " ".repeat(block_indent), + &stmts_and_call_snippet, + " ".repeat(call_expr_indent) + ); + } + stmts_and_call_snippet +} + fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -857,37 +895,15 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); - let indent = indent_of(cx, expr.span).unwrap_or(0); - if let Some(expr_str) = snippet_opt(cx, expr.span) { - let expr_with_replacements = arg_snippets - .iter() - .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); - - // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - let wrap_in_block = - !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); - - let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; - let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); - stmts_and_call.push(expr_with_replacements); - let mut stmts_and_call_str = stmts_and_call - .into_iter() - .enumerate() - .map(|(i, v)| { - let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; - trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() - }) - .collect::>() - .join(";\n"); - - if wrap_in_block { - stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; - stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); - } - - let sugg = stmts_and_call_str; + if let Some(call_snippet) = snippet_opt(cx, expr.span) { + let sugg = fmt_stmts_and_call( + cx, + expr, + &call_snippet, + &arg_snippets, + &arg_snippets_without_empty_blocks, + ); if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4ce4cdeefb4..f394b980127 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -19,6 +19,7 @@ pub mod paths; pub mod ptr; pub mod sugg; pub mod usage; + pub use self::attrs::*; pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; @@ -108,6 +109,7 @@ pub fn in_macro(span: Span) -> bool { false } } + // If the snippet is empty, it's an attribute that was inserted during macro // expansion and we want to ignore those, because they could come from external // sources that the user has no control over. @@ -571,7 +573,7 @@ pub fn snippet_block<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet(cx, span, default); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of @@ -585,7 +587,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet_with_applicability(cx, span, default, applicability); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -661,16 +663,16 @@ pub fn expr_block<'a, T: LintContext>( } } -/// Trim indentation from a multiline string with possibility of ignoring the -/// first line. -pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { - let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); - let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); - trim_multiline_inner(s_tab, ignore_first, indent, ' ') +/// Reindent a multiline string with possibility of ignoring the first line. +#[allow(clippy::needless_pass_by_value)] +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { + let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); + let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); + reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into() } -fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { - let mut x = s +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, ch: char) -> String { + let x = s .lines() .skip(ignore_first as usize) .filter_map(|l| { @@ -683,26 +685,20 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option 0 { - Cow::Owned( - s.lines() - .enumerate() - .map(|(i, l)| { - if (ignore_first && i == 0) || l.is_empty() { - l - } else { - l.split_at(x).1 - } - }) - .collect::>() - .join("\n"), - ) - } else { - s - } + let indent = indent.unwrap_or(0); + s.lines() + .enumerate() + .map(|(i, l)| { + if (ignore_first && i == 0) || l.is_empty() { + l.to_owned() + } else if x > indent { + l.split_at(x - indent).1.to_owned() + } else { + " ".repeat(indent - x) + l + } + }) + .collect::>() + .join("\n") } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -1475,26 +1471,26 @@ macro_rules! unwrap_cargo_metadata { #[cfg(test)] mod test { - use super::{trim_multiline, without_block_comments}; + use super::{reindent_multiline, without_block_comments}; #[test] - fn test_trim_multiline_single_line() { - assert_eq!("", trim_multiline("".into(), false, None)); - assert_eq!("...", trim_multiline("...".into(), false, None)); - assert_eq!("...", trim_multiline(" ...".into(), false, None)); - assert_eq!("...", trim_multiline("\t...".into(), false, None)); - assert_eq!("...", trim_multiline("\t\t...".into(), false, None)); + fn test_reindent_multiline_single_line() { + assert_eq!("", reindent_multiline("".into(), false, None)); + assert_eq!("...", reindent_multiline("...".into(), false, None)); + assert_eq!("...", reindent_multiline(" ...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t\t...".into(), false, None)); } #[test] #[rustfmt::skip] - fn test_trim_multiline_block() { + fn test_reindent_multiline_block() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { z @@ -1504,7 +1500,7 @@ mod test { \ty } else { \tz - }", trim_multiline(" if x { + }", reindent_multiline(" if x { \ty } else { \tz @@ -1513,14 +1509,14 @@ mod test { #[test] #[rustfmt::skip] - fn test_trim_multiline_empty_line() { + fn test_reindent_multiline_empty_line() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { @@ -1528,6 +1524,22 @@ mod test { }".into(), false, None)); } + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_lines_deeper() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline("\ + if x { + y + } else { + z + }".into(), true, Some(8))); + } + #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); From 2387f68e437bf2ff5f117f63936257ce64052cfa Mon Sep 17 00:00:00 2001 From: Ricky Date: Wed, 2 Sep 2020 19:21:34 -0400 Subject: [PATCH 0546/1110] Removed map_err suggestion in lint, and updated lint documentation example --- clippy_lints/src/map_err_ignore.rs | 117 ++++++++++++++--------------- tests/ui/map_err.stderr | 5 +- 2 files changed, 60 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 43bfcf0b8f1..9211113ed04 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,6 +1,6 @@ -use crate::utils::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind, QPath}; +use crate::utils::span_lint_and_help; + +use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -12,33 +12,58 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// + /// Before: /// ```rust + /// use std::convert::TryFrom; + /// + /// #[derive(Debug)] /// enum Errors { - /// Ignore - ///} - ///fn main() -> Result<(), Errors> { + /// Ignored + /// } /// - /// let x = u32::try_from(-123_i32); + /// fn divisible_by_3(inp: i32) -> Result { + /// let i = u32::try_from(inp).map_err(|_| Errors::Ignored)?; /// - /// println!("{:?}", x.map_err(|_| Errors::Ignore)); + /// Ok(i) + /// } + /// ``` /// - /// Ok(()) - ///} - /// ``` - /// Use instead: - /// ```rust - /// enum Errors { - /// WithContext(TryFromIntError) - ///} - ///fn main() -> Result<(), Errors> { + /// After: + /// ```rust + /// use std::convert::TryFrom; + /// use std::num::TryFromIntError; + /// use std::fmt; + /// use std::error::Error; /// - /// let x = u32::try_from(-123_i32); + /// #[derive(Debug)] + /// enum ParseError { + /// Indivisible { + /// source: TryFromIntError, + /// input: String, + /// } + /// } /// - /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); + /// impl fmt::Display for ParseError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// match &self { + /// ParseError::Indivisible{source: _, input} => write!(f, "Error: {}", input) + /// } + /// } + /// } /// - /// Ok(()) - ///} + /// impl Error for ParseError {} + /// + /// impl ParseError { + /// fn new(source: TryFromIntError, input: String) -> ParseError { + /// ParseError::Indivisible{source, input} + /// } + /// } + /// + /// fn divisible_by_3(inp: i32) -> Result { + /// let i = u32::try_from(inp).map_err(|e| ParseError::new(e, e.to_string()))?; + /// + /// Ok(i) + /// } /// ``` pub MAP_ERR_IGNORE, style, @@ -69,44 +94,16 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { if closure_body.params.len() == 1 { // make sure that parameter is the wild token (`_`) if let PatKind::Wild = closure_body.params[0].pat.kind { - // Check the value of the closure to see if we can build the enum we are throwing away - // the error for make sure this is a Path - if let ExprKind::Path(q_path) = &closure_body.value.kind { - // this should be a resolved path, only keep the path field - if let QPath::Resolved(_, path) = q_path { - // finally get the idents for each path segment collect them as a string and - // join them with the path separator ("::"") - let closure_fold: String = path - .segments - .iter() - .map(|x| x.ident.as_str().to_string()) - .collect::>() - .join("::"); - //Span the body of the closure (the |...| bit) and suggest the fix by taking - // the error and encapsulating it in the enum - span_lint_and_sugg( - cx, - MAP_ERR_IGNORE, - body_span, - "`map_err` has thrown away the original error", - "Allow the error enum to encapsulate the original error", - format!("|e| {}(e)", closure_fold), - Applicability::HasPlaceholders, - ); - } - } else { - //If we cannot build the enum in a human readable way just suggest not throwing way - // the error - span_lint_and_sugg( - cx, - MAP_ERR_IGNORE, - body_span, - "`map_err` has thrown away the original error", - "Allow the error enum to encapsulate the original error", - "|e|".to_string(), - Applicability::HasPlaceholders, - ); - } + // span the area of the closure capture and warn that the + // original error will be thrown away + span_lint_and_help( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_else(|_|...` ignores the original error", + None, + "Consider wrapping the error in an enum variant", + ); } } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8cd37d8c025..7a269ab95ab 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,10 +1,11 @@ -error: `map_err` has thrown away the original error +error: `map_else(|_|...` ignores the original error --> $DIR/map_err.rs:21:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); - | ^^^ help: Allow the error enum to encapsulate the original error: `|e| Errors::Ignored(e)` + | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` + = help: Consider wrapping the error in an enum variant error: aborting due to previous error From 93ce686b5df94f52a040a05c9434b4341efffec5 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 3 Sep 2020 04:58:14 +0200 Subject: [PATCH 0547/1110] Update ui stderr with improved rustc output Related rust pull request: rust-lang/rust#76160 --- tests/ui/issue-3145.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/issue-3145.stderr b/tests/ui/issue-3145.stderr index cb0d95f5e26..8f2922b022a 100644 --- a/tests/ui/issue-3145.stderr +++ b/tests/ui/issue-3145.stderr @@ -1,4 +1,4 @@ -error: expected token: `,` +error: expected `,`, found `a` --> $DIR/issue-3145.rs:2:19 | LL | println!("{}" a); //~ERROR expected token: `,` From cf1cc7c449e881f0ac5d8474209222bc269df250 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 4 Sep 2020 05:15:31 +0200 Subject: [PATCH 0548/1110] Simplify `clippy::default_trait_access` Remove repeated matching on the same QPath. --- clippy_lints/src/default_trait_access.rs | 46 +++++++++--------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 0b0a1307876..320a2a257bd 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -38,37 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; then { - match qpath { - QPath::Resolved(..) => { - if_chain! { - // Detect and ignore ::default() because these calls do - // explicitly name the type. - if let ExprKind::Call(ref method, ref _args) = expr.kind; - if let ExprKind::Path(ref p) = method.kind; - if let QPath::Resolved(Some(_ty), _path) = p; - then { - return; - } - } - - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind { - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - }, - QPath::TypeRelative(..) | QPath::LangItem(..) => {}, + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } } From 7bcf40a13d90952d9a59ed547832155439e3bcf7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 23:30:06 +0200 Subject: [PATCH 0549/1110] Fix fallout from rustup --- clippy_lints/src/default_trait_access.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 320a2a257bd..3048436d9a7 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let QPath::Resolved(None, _path) = qpath; then { let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind { + if let ty::Adt(def, ..) = expr_ty.kind() { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); From 2905fff93659d17f1c6b1cf270b5731e04ebe46d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 23:30:55 +0200 Subject: [PATCH 0550/1110] Run cargo dev fmt --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 96e9d0f32f8..bea0a4d2459 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1432,7 +1432,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option false, }; From c31d4730b0d40c62934839405d0c25e2ffa3fad1 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 4 Sep 2020 15:55:13 -0700 Subject: [PATCH 0551/1110] update example to be more idiomatic --- clippy_lints/src/map_err_ignore.rs | 87 ++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 9211113ed04..649de4133e0 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -14,55 +14,88 @@ declare_clippy_lint! { /// **Example:** /// Before: /// ```rust - /// use std::convert::TryFrom; + /// use std::fmt; /// /// #[derive(Debug)] - /// enum Errors { - /// Ignored + /// enum Error { + /// Indivisible, + /// Remainder(u8), /// } /// - /// fn divisible_by_3(inp: i32) -> Result { - /// let i = u32::try_from(inp).map_err(|_| Errors::Ignored)?; + /// impl fmt::Display for Error { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// match self { + /// Error::Indivisible => write!(f, "could not divide input by three"), + /// Error::Remainder(remainder) => write!( + /// f, + /// "input is not divisible by three, remainder = {}", + /// remainder + /// ), + /// } + /// } + /// } /// - /// Ok(i) + /// impl std::error::Error for Error {} + /// + /// fn divisible_by_3(input: &str) -> Result<(), Error> { + /// input + /// .parse::() + /// .map_err(|_| Error::Indivisible) + /// .map(|v| v % 3) + /// .and_then(|remainder| { + /// if remainder == 0 { + /// Ok(()) + /// } else { + /// Err(Error::Remainder(remainder as u8)) + /// } + /// }) /// } /// ``` /// /// After: /// ```rust - /// use std::convert::TryFrom; - /// use std::num::TryFromIntError; - /// use std::fmt; - /// use std::error::Error; + /// use std::{fmt, num::ParseIntError}; /// /// #[derive(Debug)] - /// enum ParseError { - /// Indivisible { - /// source: TryFromIntError, - /// input: String, - /// } + /// enum Error { + /// Indivisible(ParseIntError), + /// Remainder(u8), /// } /// - /// impl fmt::Display for ParseError { + /// impl fmt::Display for Error { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// match &self { - /// ParseError::Indivisible{source: _, input} => write!(f, "Error: {}", input) + /// match self { + /// Error::Indivisible(_) => write!(f, "could not divide input by three"), + /// Error::Remainder(remainder) => write!( + /// f, + /// "input is not divisible by three, remainder = {}", + /// remainder + /// ), /// } /// } /// } /// - /// impl Error for ParseError {} - /// - /// impl ParseError { - /// fn new(source: TryFromIntError, input: String) -> ParseError { - /// ParseError::Indivisible{source, input} + /// impl std::error::Error for Error { + /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + /// match self { + /// Error::Indivisible(source) => Some(source), + /// _ => None, + /// } /// } /// } /// - /// fn divisible_by_3(inp: i32) -> Result { - /// let i = u32::try_from(inp).map_err(|e| ParseError::new(e, e.to_string()))?; - /// - /// Ok(i) + /// fn divisible_by_3(input: &str) -> Result<(), Error> { + /// input + /// .parse::() + /// .map_err(Error::Indivisible) + /// .map(|v| v % 3) + /// .and_then(|remainder| { + /// if remainder == 0 { + /// Ok(()) + /// } else { + /// Err(Error::Remainder(remainder as u8)) + /// } + /// }) /// } /// ``` pub MAP_ERR_IGNORE, From 04ba07bdc3c82aa17297e3e91fdc884983be7cad Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 19:26:35 -0600 Subject: [PATCH 0552/1110] add line_count and max_lines to too_many_lines lint message --- clippy_lints/src/functions.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 89fde1d509d..50b39cf4ea7 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -374,7 +374,12 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines") + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!("this function has too many lines ({}/{})", line_count, self.max_lines), + ) } } From db16fa26cef82fff84751dcad7940fd9b819a169 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 20:16:02 -0600 Subject: [PATCH 0553/1110] run tests/ui/update-all-references.sh --- tests/ui/functions_maxlines.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/functions_maxlines.stderr b/tests/ui/functions_maxlines.stderr index c640c82d6d7..dc6c8ba2f15 100644 --- a/tests/ui/functions_maxlines.stderr +++ b/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: this function has a large number of lines +error: this function has too many lines (102/100) --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { From 9e7ce9d3851426f59ec98eabee7f113e4fd18198 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 21:12:16 -0600 Subject: [PATCH 0554/1110] run the specific script suggested by the error message ``` ./tests/ui-toml/update-references.sh './target/debug/test_build_base' 'functions_maxlines/test.rs' ``` --- tests/ui-toml/functions_maxlines/test.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index fb12257021a..a27ce945ca5 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: this function has a large number of lines +error: this function has too many lines (2/1) --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: this function has a large number of lines +error: this function has too many lines (2/1) --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { From 96b31a5b36ed06b6781804d3384a3a84a52b8ce6 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 6 Sep 2020 00:02:35 +0900 Subject: [PATCH 0555/1110] Fix FP when coercion kicks in --- clippy_lints/src/loops.rs | 3 ++- tests/ui/same_item_push.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 25345f8fa31..b65ae15edcd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,7 +1140,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - let ty = cx.typeck_results().expr_ty(pushed_item); + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); if cx .tcx .lang_items() diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0903a873826..0928820892b 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -100,4 +100,15 @@ fn main() { for _ in 0..10 { vec14.push(std::fs::File::open("foobar").unwrap()); } + // Fix #5979 + #[derive(Clone)] + struct S {} + + trait T {} + impl T for S {} + + let mut vec15: Vec> = Vec::new(); + for _ in 0..10 { + vec15.push(Box::new(S {})); + } } From 390a13b06c79d4177b829097b06453e38188081f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 23:22:32 +0200 Subject: [PATCH 0556/1110] needless-lifetime - fix nested elision site FPs --- clippy_lints/src/lifetimes.rs | 69 ++++++++++++++++++++++++++++-- clippy_lints/src/utils/paths.rs | 3 ++ tests/ui/needless_lifetimes.rs | 32 ++++++++++++++ tests/ui/needless_lifetimes.stderr | 8 +--- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4df6827d77f..ab6e34d201c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,13 +1,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_trait_ref, walk_ty, NestedVisitorMap, + Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, - TyKind, WhereClause, WherePredicate, + ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, + TraitRef, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; @@ -15,7 +16,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; -use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -127,6 +129,14 @@ fn check_fn_inner<'tcx>( return; } + // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not + // support nested elision sites in a fn item. + if FnPointerOrClosureTraitBoundFinder::find_in_generics(cx, generics) + || FnPointerOrClosureTraitBoundFinder::find_in_fn_decl(cx, decl) + { + return; + } + let mut bounds_lts = Vec::new(); let types = generics .params @@ -523,3 +533,54 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { NestedVisitorMap::None } } + +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + +struct FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: bool, +} + +impl<'a, 'tcx> FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + fn find_in_generics(cx: &'a LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> bool { + let mut finder = Self { cx, found: false }; + finder.visit_generics(generics); + finder.found + } + + fn find_in_fn_decl(cx: &'a LateContext<'tcx>, generics: &'tcx FnDecl<'tcx>) -> bool { + let mut finder = Self { cx, found: false }; + finder.visit_fn_decl(generics); + finder.found + } +} + +impl<'a, 'tcx> Visitor<'tcx> for FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_trait_ref(&mut self, tref: &'tcx TraitRef<'tcx>) { + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| tref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + self.found = true; + } + walk_trait_ref(self, tref); + } + + fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { + match ty.kind { + TyKind::BareFn(..) => self.found = true, + TyKind::OpaqueDef(item_id, _) => { + let map = self.cx.tcx.hir(); + let item = map.expect_item(item_id.id); + self.visit_item(item); + }, + _ => (), + } + walk_ty(self, ty); + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index d44854aefe9..9837759fd5e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,6 +42,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; +pub const FN: [&str; 3] = ["core", "ops", "Fn"]; +pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; +pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 913cd004f19..bc725a645ac 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,4 +259,36 @@ mod issue4291 { } } +mod nested_elision_sites { + // Don't lint these cases, they cause FPs. + // The lint does not support nested elision sites. + + fn nested_fn_trait_bound<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + + fn nested_fn_mut_trait_bound<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + + fn nested_fn_once_trait_bound<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + fn nested_generic_fn_trait_bound<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + + fn nested_where_clause_fn_trait_bound<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + + fn nested_pointer_fn<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b5..b1943bf9d70 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -36,12 +36,6 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:86:1 - | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:120:5 | @@ -102,5 +96,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors From 9fa9208bd5c3725f98dd88f761956c01ce5fd527 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 11:56:54 +0200 Subject: [PATCH 0557/1110] Restrict unnecessary_sort_by to non-ref copy types --- clippy_lints/src/unnecessary_sort_by.rs | 13 ++++++-- tests/ui/unnecessary_sort_by.fixed | 42 ++++++++++++++++++++++--- tests/ui/unnecessary_sort_by.rs | 42 ++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 8b00d29acb5..9b6a9075a29 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ use crate::utils; -use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -171,12 +170,22 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, + // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested + // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more + // than one level of references would add some extra complexity as we would have to compensate + // in the closure body. + if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC); + let vec_ty = cx.typeck_results().expr_ty(vec); + if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec + if !matches!(&ty.kind(), ty::Ref(..)); + if utils::is_copy(cx, ty); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 31c2ba0f9c5..ad0d0387db0 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index a3c8ae468ed..9746f6e6849 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } From 3d30ef7818b9ed562f2c48f3d6d15d90253ae732 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Sep 2020 22:17:31 +0900 Subject: [PATCH 0558/1110] Restrict `same_item_push` to suppress false positives It emits a lint when the pushed item is a literal, a constant and an immutable binding that are initialized with those. --- clippy_lints/src/loops.rs | 83 ++++++++++++++++++++++++++-------- tests/ui/same_item_push.rs | 13 ++++++ tests/ui/same_item_push.stderr | 34 ++++++++------ 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f13e369907b..f417e3a0caf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1153,27 +1153,70 @@ fn detect_same_item_push<'tcx>( let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } + match qpath_res(cx, qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + if_chain! { + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + }, + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ), + _ => {}, } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + } else if let ExprKind::Lit(..) = pushed_item.kind { + // literal span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0928820892b..bd4792c4a76 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -1,5 +1,7 @@ #![warn(clippy::same_item_push)] +const VALUE: u8 = 7; + fn mutate_increment(x: &mut u8) -> u8 { *x += 1; *x @@ -111,4 +113,15 @@ fn main() { for _ in 0..10 { vec15.push(Box::new(S {})); } + + let mut vec16 = Vec::new(); + for _ in 0..20 { + vec16.push(VALUE); + } + + let mut vec17 = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec17.push(item); + } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index ddc5d48cd41..4896479791a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,22 +1,14 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:16:9 - | -LL | spaces.push(vec![b' ']); - | ^^^^^^ - | - = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) - -error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:22:9 + --> $DIR/same_item_push.rs:24:9 | LL | vec2.push(item); | ^^^^ | + = note: `-D clippy::same-item-push` implied by `-D warnings` = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:28:9 + --> $DIR/same_item_push.rs:30:9 | LL | vec3.push(item); | ^^^^ @@ -24,12 +16,28 @@ LL | vec3.push(item); = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:33:9 + --> $DIR/same_item_push.rs:35:9 | LL | vec4.push(13); | ^^^^ | = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) -error: aborting due to 4 previous errors +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:119:9 + | +LL | vec16.push(VALUE); + | ^^^^^ + | + = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:125:9 + | +LL | vec17.push(item); + | ^^^^^ + | + = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + +error: aborting due to 5 previous errors From 619ca76731e7209288fd3ebf1ae243c050486c12 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:25:31 +0900 Subject: [PATCH 0559/1110] Refactoring: use inner function --- clippy_lints/src/loops.rs | 69 ++++++++++++--------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f417e3a0caf..c27acdd2236 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1150,8 +1150,6 @@ fn detect_same_item_push<'tcx>( { // Make sure that the push does not involve possibly mutating values if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant @@ -1167,33 +1165,11 @@ fn detect_same_item_push<'tcx>( then { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } _ => {}, @@ -1202,37 +1178,34 @@ fn detect_same_item_push<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } } else if let ExprKind::Lit(..) = pushed_item.kind { // literal - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } } } } + + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } /// Checks for looping over a range and then indexing a sequence with it. From 5d085ad011a602e004e7d33fa0b9074c7dab36fc Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:34:51 +0900 Subject: [PATCH 0560/1110] Some refactoring --- clippy_lints/src/loops.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c27acdd2236..c59185bd7bd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,10 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + if !matches!(pat.kind, PatKind::Wild) { + return; + } + // Determine whether it is safe to lint the body let mut same_item_push_visitor = SameItemPushVisitor { should_lint: true, @@ -1149,8 +1153,8 @@ fn detect_same_item_push<'tcx>( .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - if let ExprKind::Path(ref qpath) = pushed_item.kind { + match pushed_item.kind { + ExprKind::Path(ref qpath) => { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant Res::Local(hir_id) => { @@ -1161,7 +1165,7 @@ fn detect_same_item_push<'tcx>( if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); let parent_node = cx.tcx.hir().get_parent_node(hir_id); if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + if let Some(init) = parent_let_expr.init; then { match init.kind { // immutable bindings that are initialized with literal @@ -1181,10 +1185,9 @@ fn detect_same_item_push<'tcx>( Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } - } else if let ExprKind::Lit(..) = pushed_item.kind { - // literal - emit_lint(cx, vec, pushed_item); - } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, } } } From a60e5de90c7370d4fb3e6561d3cb55495cda2e2a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 8 Sep 2020 02:39:39 +0200 Subject: [PATCH 0561/1110] needless-lifetime / nested elision sites / PR remarks --- clippy_lints/src/lifetimes.rs | 222 ++++++------------ tests/ui/crashes/ice-2774.stderr | 10 + .../needless_lifetimes_impl_trait.stderr | 14 ++ tests/ui/needless_lifetimes.stderr | 8 +- 4 files changed, 102 insertions(+), 152 deletions(-) create mode 100644 tests/ui/crashes/ice-2774.stderr create mode 100644 tests/ui/crashes/needless_lifetimes_impl_trait.stderr diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ab6e34d201c..1d17fbd3725 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,23 +1,22 @@ +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_trait_ref, walk_ty, NestedVisitorMap, - Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, + NestedVisitorMap, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, - TraitRef, Ty, TyKind, WhereClause, WherePredicate, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; - -use crate::utils::paths; -use crate::utils::{get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -110,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } /// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash, Debug, Clone)] enum RefLt { Unnamed, Static, @@ -129,15 +128,6 @@ fn check_fn_inner<'tcx>( return; } - // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not - // support nested elision sites in a fn item. - if FnPointerOrClosureTraitBoundFinder::find_in_generics(cx, generics) - || FnPointerOrClosureTraitBoundFinder::find_in_fn_decl(cx, decl) - { - return; - } - - let mut bounds_lts = Vec::new(); let types = generics .params .iter() @@ -166,13 +156,12 @@ fn check_fn_inner<'tcx>( if bound.name != LifetimeName::Static && !bound.is_elided() { return; } - bounds_lts.push(bound); } } } } } - if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { + if could_use_elision(cx, decl, body, &generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -191,7 +180,6 @@ fn could_use_elision<'tcx>( func: &'tcx FnDecl<'_>, body: Option, named_generics: &'tcx [GenericParam<'_>], - bounds_lts: Vec<&'tcx Lifetime>, ) -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -214,15 +202,31 @@ fn could_use_elision<'tcx>( if let Return(ref ty) = func.output { output_visitor.visit_ty(ty); } + for lt in named_generics { + input_visitor.visit_generic_param(lt) + } - let input_lts = match input_visitor.into_vec() { - Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), - None => return false, - }; - let output_lts = match output_visitor.into_vec() { - Some(val) => val, - None => return false, - }; + if input_visitor.abort() || output_visitor.abort() { + return false; + } + + if allowed_lts + .intersection(&FxHashSet::from_iter( + input_visitor + .nested_elision_site_lts + .iter() + .chain(output_visitor.nested_elision_site_lts.iter()) + .cloned() + .filter(|v| matches!(v, RefLt::Named(_))), + )) + .next() + .is_some() + { + return false; + } + + let input_lts = input_visitor.lts; + let output_lts = output_visitor.lts; if let Some(body_id) = body { let mut checker = BodyLifetimeChecker { @@ -287,27 +291,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { allowed_lts } -fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { - for lt in bounds_lts { - if lt.name != LifetimeName::Static { - vec.push(RefLt::Named(lt.name.ident().name)); - } - } - - vec -} - /// Number of unique lifetimes in the given vector. #[must_use] fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() } +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, - abort: bool, + nested_elision_site_lts: Vec, + unelided_trait_object_lifetime: bool, } impl<'a, 'tcx> RefVisitor<'a, 'tcx> { @@ -315,7 +312,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - abort: false, + nested_elision_site_lts: Vec::new(), + unelided_trait_object_lifetime: false, } } @@ -335,40 +333,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } - fn into_vec(self) -> Option> { - if self.abort { - None - } else { - Some(self.lts) - } + fn all_lts(&self) -> Vec { + self.lts + .iter() + .chain(self.nested_elision_site_lts.iter()) + .cloned() + .collect::>() } - fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { - if let Some(ref last_path_segment) = last_path_segment(qpath).args { - if !last_path_segment.parenthesized - && !last_path_segment - .args - .iter() - .any(|arg| matches!(arg, GenericArg::Lifetime(_))) - { - let hir_id = ty.hir_id; - match self.cx.qpath_res(qpath, hir_id) { - Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => { - let generics = self.cx.tcx.generics_of(def_id); - for _ in generics.params.as_slice() { - self.record(&None); - } - }, - Res::Def(DefKind::Trait, def_id) => { - let trait_def = self.cx.tcx.trait_def(def_id); - for _ in &self.cx.tcx.generics_of(trait_def.def_id).params { - self.record(&None); - } - }, - _ => (), - } - } - } + fn abort(&self) -> bool { + self.unelided_trait_object_lifetime } } @@ -380,30 +354,36 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { self.record(&Some(*lifetime)); } + fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) { + let trait_ref = &poly_tref.trait_ref; + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_trait_ref(trait_ref); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + } else { + walk_poly_trait_ref(self, poly_tref, tbm); + } + } + fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::Rptr(ref lt, _) if lt.is_elided() => { - self.record(&None); - }, - TyKind::Path(ref path) => { - self.collect_anonymous_lifetimes(path, ty); - }, TyKind::OpaqueDef(item, _) => { let map = self.cx.tcx.hir(); - if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { - for bound in exist_ty.bounds { - if let GenericBound::Outlives(_) = *bound { - self.record(&None); - } - } - } else { - unreachable!() - } + let item = map.expect_item(item.id); + walk_item(self, item); walk_ty(self, ty); }, + TyKind::BareFn(&BareFnTy { decl, .. }) => { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_fn_decl(decl); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { - self.abort = true; + self.unelided_trait_object_lifetime = true; } for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); @@ -440,16 +420,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - match visitor.into_vec() { - None => return false, - Some(lts) => { - for lt in lts { - if !allowed_lts.contains(<) { - return true; - } - } - }, - } + return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); @@ -533,54 +504,3 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { NestedVisitorMap::None } } - -const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; - -struct FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: bool, -} - -impl<'a, 'tcx> FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - fn find_in_generics(cx: &'a LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> bool { - let mut finder = Self { cx, found: false }; - finder.visit_generics(generics); - finder.found - } - - fn find_in_fn_decl(cx: &'a LateContext<'tcx>, generics: &'tcx FnDecl<'tcx>) -> bool { - let mut finder = Self { cx, found: false }; - finder.visit_fn_decl(generics); - finder.found - } -} - -impl<'a, 'tcx> Visitor<'tcx> for FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_trait_ref(&mut self, tref: &'tcx TraitRef<'tcx>) { - if CLOSURE_TRAIT_BOUNDS - .iter() - .any(|trait_path| tref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) - { - self.found = true; - } - walk_trait_ref(self, tref); - } - - fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { - match ty.kind { - TyKind::BareFn(..) => self.found = true, - TyKind::OpaqueDef(item_id, _) => { - let map = self.cx.tcx.hir(); - let item = map.expect_item(item_id.id); - self.visit_item(item); - }, - _ => (), - } - walk_ty(self, ty); - } -} diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr new file mode 100644 index 00000000000..fd502cba73a --- /dev/null +++ b/tests/ui/crashes/ice-2774.stderr @@ -0,0 +1,10 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/ice-2774.rs:17:1 + | +LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr new file mode 100644 index 00000000000..02b86397ed5 --- /dev/null +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -0,0 +1,14 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes_impl_trait.rs:17:5 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/needless_lifetimes_impl_trait.rs:3:9 + | +LL | #![deny(clippy::needless_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index b1943bf9d70..d3a360ed8b5 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -36,6 +36,12 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:86:1 + | +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:120:5 | @@ -96,5 +102,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors From 2c9f82e7d22a5cd3f704ed0b932a17fa53f1145b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 22:45:27 +0900 Subject: [PATCH 0562/1110] Add some tests to `same_item_push` Add tests in which the variable is initialized with a match expression and function call --- tests/ui/same_item_push.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bd4792c4a76..7ca829854db 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -11,6 +11,10 @@ fn increment(x: u8) -> u8 { x + 1 } +fn fun() -> usize { + 42 +} + fn main() { // Test for basic case let mut spaces = Vec::with_capacity(10); @@ -124,4 +128,21 @@ fn main() { for _ in 0..20 { vec17.push(item); } + + let mut vec18 = Vec::new(); + let item = 42; + let item = fun(); + for _ in 0..20 { + vec18.push(item); + } + + let mut vec19 = Vec::new(); + let key = 1; + for _ in 0..20 { + let item = match key { + 1 => 10, + _ => 0, + }; + vec19.push(item); + } } From 952c04674e4225d231f0ba78dff32abf7c06cb8b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:03:15 +0900 Subject: [PATCH 0563/1110] Refactoring: tests in `same_item_push` --- tests/ui/same_item_push.rs | 111 +++++++++++++++++---------------- tests/ui/same_item_push.stderr | 40 ++++++------ 2 files changed, 77 insertions(+), 74 deletions(-) diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 7ca829854db..a37c8782ec3 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -16,64 +16,76 @@ fn fun() -> usize { } fn main() { - // Test for basic case + // ** linted cases ** + let mut vec: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + vec.push(13); + } + + let mut vec = Vec::new(); + for _ in 0..20 { + vec.push(VALUE); + } + + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + } + + // ** non-linted cases ** let mut spaces = Vec::with_capacity(10); for _ in 0..10 { spaces.push(vec![b' ']); } - let mut vec2: Vec = Vec::new(); - let item = 2; - for _ in 5..=20 { - vec2.push(item); - } - - let mut vec3: Vec = Vec::new(); - for _ in 0..15 { - let item = 2; - vec3.push(item); - } - - let mut vec4: Vec = Vec::new(); - for _ in 0..15 { - vec4.push(13); - } - // Suggestion should not be given as pushed variable can mutate - let mut vec5: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; for _ in 0..30 { - vec5.push(mutate_increment(&mut item)); + vec.push(mutate_increment(&mut item)); } - let mut vec6: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; let mut item2 = &mut mutate_increment(&mut item); for _ in 0..30 { - vec6.push(mutate_increment(item2)); + vec.push(mutate_increment(item2)); } - let mut vec7: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { - vec7.push(a); + vec.push(a); } - let mut vec8: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec8.push(increment(i)); + vec.push(increment(i)); } - let mut vec9: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec9.push(i + i * i); + vec.push(i + i * i); } // Suggestion should not be given as there are multiple pushes that are not the same - let mut vec10: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let item: u8 = 2; for _ in 0..30 { - vec10.push(item); - vec10.push(item * 2); + vec.push(item); + vec.push(item * 2); } // Suggestion should not be given as Vec is not involved @@ -88,23 +100,23 @@ fn main() { for i in 0..30 { vec_a.push(A { kind: i }); } - let mut vec12: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for a in vec_a { - vec12.push(2u8.pow(a.kind)); + vec.push(2u8.pow(a.kind)); } // Fix #5902 - let mut vec13: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item = 0; for _ in 0..10 { - vec13.push(item); + vec.push(item); item += 10; } // Fix #5979 - let mut vec14: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for _ in 0..10 { - vec14.push(std::fs::File::open("foobar").unwrap()); + vec.push(std::fs::File::open("foobar").unwrap()); } // Fix #5979 #[derive(Clone)] @@ -113,36 +125,27 @@ fn main() { trait T {} impl T for S {} - let mut vec15: Vec> = Vec::new(); + let mut vec: Vec> = Vec::new(); for _ in 0..10 { - vec15.push(Box::new(S {})); + vec.push(Box::new(S {})); } - let mut vec16 = Vec::new(); - for _ in 0..20 { - vec16.push(VALUE); - } - - let mut vec17 = Vec::new(); - let item = VALUE; - for _ in 0..20 { - vec17.push(item); - } - - let mut vec18 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let item = 42; let item = fun(); for _ in 0..20 { - vec18.push(item); + vec.push(item); } - let mut vec19 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let key = 1; for _ in 0..20 { let item = match key { 1 => 10, _ => 0, }; - vec19.push(item); + vec.push(item); } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 4896479791a..d9ffa15780a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,43 +1,43 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:24:9 + --> $DIR/same_item_push.rs:23:9 | -LL | vec2.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:30:9 + --> $DIR/same_item_push.rs:29:9 | -LL | vec3.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:35:9 + --> $DIR/same_item_push.rs:34:9 | -LL | vec4.push(13); - | ^^^^ +LL | vec.push(13); + | ^^^ | - = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:119:9 + --> $DIR/same_item_push.rs:39:9 | -LL | vec16.push(VALUE); - | ^^^^^ +LL | vec.push(VALUE); + | ^^^ | - = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:125:9 + --> $DIR/same_item_push.rs:45:9 | -LL | vec17.push(item); - | ^^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: aborting due to 5 previous errors From 1d8ae3aa12567b8461436ac10ba9fc4da55adb29 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:12:04 +0900 Subject: [PATCH 0564/1110] Address `items_after_statement` --- clippy_lints/src/loops.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c59185bd7bd..6c54c07869a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,23 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + if !matches!(pat.kind, PatKind::Wild) { return; } @@ -1192,23 +1209,6 @@ fn detect_same_item_push<'tcx>( } } } - - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } } /// Checks for looping over a range and then indexing a sequence with it. From a899ad2e125e8dd3a0a339df1f8457ef8cab3cc7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Tue, 8 Sep 2020 23:35:19 +0900 Subject: [PATCH 0565/1110] Change suggestion to `create_dir_all({})` from `std::fs::create_dir_all({})` --- clippy_lints/src/create_dir.rs | 2 +- tests/ui/create_dir.fixed | 6 ++++-- tests/ui/create_dir.rs | 2 ++ tests/ui/create_dir.stderr | 8 ++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index f80a0efa7a5..4002fb655a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { expr.span, "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", - format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + format!("create_dir_all({})", snippet(cx, args[0].span, "..")), Applicability::MaybeIncorrect, ) } diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 0e28f87e33d..8ed53a56ac0 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { // Should be warned - std::fs::create_dir_all("foo"); - std::fs::create_dir_all("bar").unwrap(); + create_dir_all("foo"); + create_dir_all("bar").unwrap(); // Shouldn't be warned create_dir(); diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 1f226298c0d..19c8fc24ba2 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,6 +2,8 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 0c97bdd0f7a..67298fc4709 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,16 +1,16 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:11:5 | LL | std::fs::create_dir("foo"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` | = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:10:5 + --> $DIR/create_dir.rs:12:5 | LL | std::fs::create_dir("bar").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` error: aborting due to 2 previous errors From 4f1c4a99d4d98f1acea3c9c7cc55355aa46119aa Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 8 Sep 2020 12:05:18 -0400 Subject: [PATCH 0566/1110] Fixed typo in lint and test --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 649de4133e0..fa59b6ec370 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` /// - /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to bubble the original error + /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error /// /// **Known problems:** None. /// @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { cx, MAP_ERR_IGNORE, body_span, - "`map_else(|_|...` ignores the original error", + "`map_err(|_|...` ignores the original error", None, "Consider wrapping the error in an enum variant", ); diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 7a269ab95ab..e4e6a289ba0 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,4 +1,4 @@ -error: `map_else(|_|...` ignores the original error +error: `map_err(|_|...` ignores the original error --> $DIR/map_err.rs:21:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); From d719b485434eac557e65bf55cca79e63f7b83d5b Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 8 Sep 2020 19:37:14 -0400 Subject: [PATCH 0567/1110] Move map_err_ignore from style to pedantic --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/map_err_ignore.rs | 2 +- src/lintlist/mod.rs | 2 +- tests/ui/map_err.rs | 1 + tests/ui/map_err.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3794cae091a..797ab62cb54 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1179,6 +1179,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(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), @@ -1330,7 +1331,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_err_ignore::MAP_ERR_IGNORE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1538,7 +1538,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index fa59b6ec370..5298e16a04d 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -99,7 +99,7 @@ declare_clippy_lint! { /// } /// ``` pub MAP_ERR_IGNORE, - style, + pedantic, "`map_err` should not ignore the original error" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6725c97f793..5105e953162 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1167,7 +1167,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "map_err_ignore", - group: "style", + group: "pedantic", desc: "`map_err` should not ignore the original error", deprecation: None, module: "map_err_ignore", diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index f3a74ad95cd..617b6422872 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,3 +1,4 @@ +#![warn(clippy::map_err_ignore)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index e4e6a289ba0..7273f460380 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` ignores the original error - --> $DIR/map_err.rs:21:32 + --> $DIR/map_err.rs:22:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ From d712d7f700977240ad9bd731fc3f76e05cc1c900 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 9 Sep 2020 14:18:19 +1200 Subject: [PATCH 0568/1110] Improve "known problems" of `interior_mutable_key` * Remove the mention to `Rc` and `Arc` as these are `Freeze` so the lint correctly handles already. * Instead, explain what could cause a false positive, and mention `bytes` as an example. --- clippy_lints/src/mut_key.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 7423107e8f9..fa3a99dad9d 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -12,8 +12,10 @@ declare_clippy_lint! { /// `BtreeSet` rely on either the hash or the order of keys be unchanging, /// so having types with interior mutability is a bad idea. /// - /// **Known problems:** We don't currently account for `Rc` or `Arc`, so - /// this may yield false positives. + /// **Known problems:** It's correct to use a struct, that contains interior mutability, + /// as a key; when its `Hash` implementation doesn't access any these interior mutable types. + /// However, this lint is unable to recognise it so cause a false positive. + /// `bytes` ctate is a great example of this. /// /// **Example:** /// ```rust From c8e9e52303da6dff4bc5cc4db3021d608fca6c70 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 8 Sep 2020 22:22:23 +0200 Subject: [PATCH 0569/1110] needless-lifetime / add test cases for nested elision sites --- tests/ui/needless_lifetimes.rs | 78 ++++++++++++++++++++++++------ tests/ui/needless_lifetimes.stderr | 32 +++++++++++- 2 files changed, 95 insertions(+), 15 deletions(-) diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index bc725a645ac..548c5929c61 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,36 +259,86 @@ mod issue4291 { } } +mod issue2944 { + trait Foo {} + struct Bar {} + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz<'a>(&'a self) -> impl Foo + 'a { + Baz { bar: self } + } + } +} + mod nested_elision_sites { - // Don't lint these cases, they cause FPs. - // The lint does not support nested elision sites. + // issue #issue2944 - fn nested_fn_trait_bound<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { move || i } - fn nested_fn_mut_trait_bound<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { - move || i - } - - fn nested_fn_once_trait_bound<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { - move || i - } - - fn nested_generic_fn_trait_bound<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { f() } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + f(i) + } - fn nested_where_clause_fn_trait_bound<'a, T>(f: T) -> &'a i32 + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 where T: Fn() -> &'a i32, { f() } + // lint + fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } - fn nested_pointer_fn<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { |i| i } + // lint + fn pointer_fn_elidable<'a>(f: fn(&i32) -> &i32, i: &'a i32) -> &'a i32 { + f(i) + } } fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b5..ac38ab8effd 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -102,5 +102,35 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:271:9 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:300:5 + | +LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:303:5 + | +LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:312:5 + | +LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:324:5 + | +LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 22 previous errors From 46b164313288c4b5454ccdaa5ebee2412855f0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 9 Sep 2020 11:57:30 +0200 Subject: [PATCH 0570/1110] update cargo dev ra-setup to changed rustc source paths --- clippy_dev/src/ra_setup.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index f2bd651ab25..c67efc10f15 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -14,7 +14,7 @@ pub fn run(rustc_path: Option<&str>) { // we can unwrap here because the arg is required here let rustc_path = PathBuf::from(rustc_path.unwrap()); assert!(rustc_path.is_dir(), "path is not a directory"); - let rustc_source_basedir = rustc_path.join("src"); + let rustc_source_basedir = rustc_path.join("compiler"); assert!( rustc_source_basedir.is_dir(), "are you sure the path leads to a rustc repo?" @@ -61,7 +61,7 @@ fn inject_deps_into_manifest( let new_deps = extern_crates.map(|dep| { // format the dependencies that are going to be put inside the Cargo.toml format!( - "{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n", + "{dep} = {{ path = \"{source_path}/{dep}\" }}\n", dep = dep, source_path = rustc_source_dir.display() ) From 4db10297c1dcd2fc368bd61a24aa0291a6b6c61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 9 Sep 2020 16:53:45 +0200 Subject: [PATCH 0571/1110] link to the Box docs in related lint documentation. --- clippy_lints/src/types.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c82deaa43b2..550ae2927a8 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -37,6 +37,7 @@ use crate::utils::{ declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` it, you just add another level of indirection @@ -65,6 +66,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. @@ -167,6 +169,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `&Box` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** Any `&Box` can also be a `&T`, which is more /// general. From de195f2d3d3a2039cb8c4141aa37d060780beaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 9 Sep 2020 17:59:13 +0200 Subject: [PATCH 0572/1110] print the unit type `()` in related lint messages. --- clippy_lints/src/map_unit_fn.rs | 6 ++-- tests/ui/option_map_unit_fn_fixable.stderr | 36 ++++++++++---------- tests/ui/result_map_unit_fn_fixable.stderr | 34 +++++++++--------- tests/ui/result_map_unit_fn_unfixable.stderr | 12 +++---- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 1f9ae8c931a..076ef235b8b 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -9,7 +9,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -51,7 +51,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `result.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -197,7 +197,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", map_type, function_type ) } diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 1312c70b6d5..d7d45ef9b0b 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); @@ -136,7 +136,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing);} diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 467e00263cd..4f3a8c6b792 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:37:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_result_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:47:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:70:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index b23cc608621..88e4efdb0f0 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); @@ -8,7 +8,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:25:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); @@ -16,7 +16,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { @@ -30,7 +30,7 @@ LL | || }); | |_______| | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:33:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); @@ -38,7 +38,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:37:5 | LL | "12".parse::().map(diverge); @@ -46,7 +46,7 @@ LL | "12".parse::().map(diverge); | | | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:43:5 | LL | y.map(do_nothing); From 3550568a548091ec4ae738827b50a84e9ddf0b8f Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Wed, 9 Sep 2020 14:02:34 -0700 Subject: [PATCH 0573/1110] removing if chain and renaming lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 8 ++--- ...nic_in_result.rs => panic_in_result_fn.rs} | 29 +++++++------------ src/lintlist/mod.rs | 4 +-- ...nic_in_result.rs => panic_in_result_fn.rs} | 2 +- ...esult.stderr => panic_in_result_fn.stderr} | 26 ++++++++--------- 6 files changed, 32 insertions(+), 39 deletions(-) rename clippy_lints/src/{panic_in_result.rs => panic_in_result_fn.rs} (79%) rename tests/ui/{panic_in_result.rs => panic_in_result_fn.rs} (97%) rename tests/ui/{panic_in_result.stderr => panic_in_result_fn.stderr} (88%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af3b666cca..d4f0ff4ba78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,7 +1651,7 @@ Released 2018-09-13 [`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 -[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b70d126af5b..dbe17e6bbfe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,7 +267,7 @@ mod open_options; mod option_env_unwrap; mod option_if_let_else; mod overflow_check_conditional; -mod panic_in_result; +mod panic_in_result_fn; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; @@ -748,7 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - &panic_in_result::PANIC_IN_RESULT, + &panic_in_result_fn::PANIC_IN_RESULT_FN, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::TODO, @@ -1088,7 +1088,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); - store.register_late_pass(|| box panic_in_result::PanicInResult); + store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { @@ -1132,7 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), - LintId::of(&panic_in_result::PANIC_IN_RESULT), + LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result_fn.rs similarity index 79% rename from clippy_lints/src/panic_in_result.rs rename to clippy_lints/src/panic_in_result_fn.rs index 8b8e211cb72..4077aba6ef1 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,5 +1,4 @@ use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::Expr; @@ -23,14 +22,14 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` - pub PANIC_IN_RESULT, + pub PANIC_IN_RESULT_FN, restriction, "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " } -declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); +declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); -impl<'tcx> LateLintPass<'tcx> for PanicInResult { +impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -40,15 +39,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { span: Span, hir_id: hir::HirId, ) { - if let FnKind::Closure(_) = fn_kind { - return; - } - if_chain! { - if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); - then - { - lint_impl_body(cx, span, body); - } + if !matches!(fn_kind, FnKind::Closure(_)) + && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) + { + lint_impl_body(cx, span, body); } } } @@ -61,10 +55,9 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if is_expn_of(expr.span, "unimplemented").is_some() - || is_expn_of(expr.span, "unreachable").is_some() - || is_expn_of(expr.span, "panic").is_some() - || is_expn_of(expr.span, "todo").is_some() + if ["unimplemented", "unreachable", "panic", "todo"] + .iter() + .any(|fun| is_expn_of(expr.span, fun).is_some()) { self.result.push(expr.span); } @@ -83,7 +76,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir if !panics.result.is_empty() { span_lint_and_then( cx, - PANIC_IN_RESULT, + PANIC_IN_RESULT_FN, impl_span, "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", move |diag| { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1f56c56f081..4d6c45761e6 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1705,11 +1705,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "panic_unimplemented", }, Lint { - name: "panic_in_result", + name: "panic_in_result_fn", group: "restriction", desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ", deprecation: None, - module: "panic_in_result", + module: "panic_in_result_fn", }, Lint { name: "panic_params", diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result_fn.rs similarity index 97% rename from tests/ui/panic_in_result.rs rename to tests/ui/panic_in_result_fn.rs index f6fb2f1ab61..287726f7a2d 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -1,4 +1,4 @@ -#![warn(clippy::panic_in_result)] +#![warn(clippy::panic_in_result_fn)] struct A; diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result_fn.stderr similarity index 88% rename from tests/ui/panic_in_result.stderr rename to tests/ui/panic_in_result_fn.stderr index 9faedf82986..c6936fd8692 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,5 +1,5 @@ error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:6:5 + --> $DIR/panic_in_result_fn.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint LL | | { @@ -7,17 +7,17 @@ LL | | panic!("error"); LL | | } | |_____^ | - = note: `-D clippy::panic-in-result` implied by `-D warnings` + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:8:9 + --> $DIR/panic_in_result_fn.rs:8:9 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:11:5 + --> $DIR/panic_in_result_fn.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint LL | | { @@ -27,14 +27,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:13:9 + --> $DIR/panic_in_result_fn.rs:13:9 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:16:5 + --> $DIR/panic_in_result_fn.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint LL | | { @@ -44,14 +44,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:18:9 + --> $DIR/panic_in_result_fn.rs:18:9 | LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:21:5 + --> $DIR/panic_in_result_fn.rs:21:5 | LL | / fn result_with_todo() -> Result // should emit lint LL | | { @@ -61,14 +61,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:23:9 + --> $DIR/panic_in_result_fn.rs:23:9 | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:52:1 + --> $DIR/panic_in_result_fn.rs:52:1 | LL | / fn function_result_with_panic() -> Result // should emit lint LL | | { @@ -78,14 +78,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:54:5 + --> $DIR/panic_in_result_fn.rs:54:5 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:67:1 + --> $DIR/panic_in_result_fn.rs:67:1 | LL | / fn main() -> Result<(), String> { LL | | todo!("finish main method"); @@ -95,7 +95,7 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:68:5 + --> $DIR/panic_in_result_fn.rs:68:5 | LL | todo!("finish main method"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From f90b1fc0636cb50bd225caeb20d6a1309d7b966f Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Wed, 9 Sep 2020 14:06:11 -0700 Subject: [PATCH 0574/1110] cargo dev update-lints --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f0ff4ba78..6b6be50065c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,7 +1651,7 @@ Released 2018-09-13 [`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 -[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl From 6211599ccafee5b2429bfb1bbeb48ead32a48484 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:00:31 -0700 Subject: [PATCH 0575/1110] Extend invalid_atomic_ordering to detect misuse of compare_exchange{,_weak} --- clippy_lints/src/atomic_ordering.rs | 82 ++++++- tests/ui/atomic_ordering_exchange.rs | 84 ++++++++ tests/ui/atomic_ordering_exchange.stderr | 259 +++++++++++++++++++++++ 3 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 tests/ui/atomic_ordering_exchange.rs create mode 100644 tests/ui/atomic_ordering_exchange.stderr diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 2d964ac2b9f..748f45f47c2 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for usage of invalid atomic - /// ordering in atomic loads/stores and memory fences. + /// ordering in atomic loads/stores/exchanges and memory fences /// /// **Why is this bad?** Using an invalid atomic ordering /// will cause a panic at run-time. @@ -29,10 +29,13 @@ declare_clippy_lint! { /// /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); + /// + /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); + /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, - "usage of invalid atomic ordering in atomic loads/stores and memory fences" + "usage of invalid atomic ordering in atomic loads/stores/exchanges ane memory fences" } declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); @@ -127,9 +130,84 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option { + if let ExprKind::Path(ref ord_qpath) = ord_arg.kind { + cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id() + } else { + None + } +} +fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + let method = method_path.ident.name.as_str(); + if type_is_atomic(cx, &args[0]); + if method == "compare_exchange" || method == "compare_exchange_weak"; + let failure_order_arg = &args[4]; + if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); + then { + // Helper type holding on to some checking and error reporting data. Has + // - (success ordering name, + // - list of failure orderings forbidden by the success order, + // - suggestion message) + type OrdLintInfo = (&'static str, &'static [&'static str], &'static str); + let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`"); + let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`"); + let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`"); + let release = ("Release", relaxed.1, relaxed.2); + let acqrel = ("AcqRel", acquire.1, acquire.2); + let search = [relaxed, acquire, seq_cst, release, acqrel]; + + let success_lint_info = opt_ordering_defid(cx, &args[3]) + .and_then(|success_ord_def_id| -> Option { + search + .iter() + .find(|(ordering, ..)| { + match_def_path(cx, success_ord_def_id, + &["core", "sync", "atomic", "Ordering", ordering]) + }) + .copied() + }); + + if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) { + // If we don't know the success order is, use what we'd suggest + // if it were maximally permissive. + let suggested = success_lint_info.unwrap_or(seq_cst).2; + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + failure_order_arg.span, + &format!( + "{}'s failure ordering may not be `Release` or `AcqRel`", + method, + ), + None, + &format!("consider using {} instead", suggested), + ); + } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info { + if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) { + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + failure_order_arg.span, + &format!( + "{}'s failure ordering may not stronger than the success ordering of `{}`", + method, + success_ord_name, + ), + None, + &format!("consider using {} instead", suggested), + ); + } + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for AtomicOrdering { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { check_atomic_load_store(cx, expr); check_memory_fence(cx, expr); + check_atomic_compare_exchange(cx, expr); } } diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs new file mode 100644 index 00000000000..2f60a98f037 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange.rs @@ -0,0 +1,84 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn main() { + // `compare_exchange` (not weak) testing + let x = AtomicUsize::new(0); + + // Allowed ordering combos + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); + + // compare_exchange_weak tests + + // Allowed ordering combos + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); +} diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr new file mode 100644 index 00000000000..26ec15be518 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -0,0 +1,259 @@ +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:21:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:22:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:23:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:24:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:25:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:28:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:29:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:30:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:31:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:32:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:35:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:36:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:39:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:40:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange.rs:43:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:44:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:60:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:61:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:62:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:63:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:64:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:67:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:68:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:69:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:70:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:71:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:74:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:75:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:78:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:79:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange.rs:82:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:83:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 32 previous errors + From 61671a2268903e1b5c28fcb3c713c27e84ea3e9b Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:12:14 -0700 Subject: [PATCH 0576/1110] Detect fetch_update misuse in invalid_atomic_ordering too --- clippy_lints/src/atomic_ordering.rs | 16 ++- src/lintlist/mod.rs | 2 +- tests/ui/atomic_ordering_fetch_update.rs | 45 +++++++ tests/ui/atomic_ordering_fetch_update.stderr | 131 +++++++++++++++++++ 4 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 tests/ui/atomic_ordering_fetch_update.rs create mode 100644 tests/ui/atomic_ordering_fetch_update.stderr diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 748f45f47c2..ff2c281ec9d 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -8,7 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for usage of invalid atomic - /// ordering in atomic loads/stores/exchanges and memory fences + /// ordering in atomic loads/stores/exchanges/updates and + /// memory fences. /// /// **Why is this bad?** Using an invalid atomic ordering /// will cause a panic at run-time. @@ -32,10 +33,11 @@ declare_clippy_lint! { /// /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); + /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val ^ val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, - "usage of invalid atomic ordering in atomic loads/stores/exchanges ane memory fences" + "usage of invalid atomic ordering in atomic operations and memory fences" } declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); @@ -142,8 +144,12 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); - if method == "compare_exchange" || method == "compare_exchange_weak"; - let failure_order_arg = &args[4]; + if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update"; + let (success_order_arg, failure_order_arg) = if method == "fetch_update" { + (&args[1], &args[2]) + } else { + (&args[3], &args[4]) + }; if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); then { // Helper type holding on to some checking and error reporting data. Has @@ -158,7 +164,7 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { let acqrel = ("AcqRel", acquire.1, acquire.2); let search = [relaxed, acquire, seq_cst, release, acqrel]; - let success_lint_info = opt_ordering_defid(cx, &args[3]) + let success_lint_info = opt_ordering_defid(cx, success_order_arg) .and_then(|success_ord_def_id| -> Option { search .iter() diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index dff19ef440f..5dead1b9b69 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -923,7 +923,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "invalid_atomic_ordering", group: "correctness", - desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences", + desc: "usage of invalid atomic ordering in atomic operations and memory fences", deprecation: None, module: "atomic_ordering", }, diff --git a/tests/ui/atomic_ordering_fetch_update.rs b/tests/ui/atomic_ordering_fetch_update.rs new file mode 100644 index 00000000000..550bdb001e4 --- /dev/null +++ b/tests/ui/atomic_ordering_fetch_update.rs @@ -0,0 +1,45 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicIsize, Ordering}; + +fn main() { + // `fetch_update` testing + let x = AtomicIsize::new(0); + + // Allowed ordering combos + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old + 1)); + + // AcqRel is always forbidden as a failure ordering + let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); + + // Release is always forbidden as a failure ordering + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); +} diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr new file mode 100644 index 00000000000..362e104a244 --- /dev/null +++ b/tests/ui/atomic_ordering_fetch_update.stderr @@ -0,0 +1,131 @@ +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:21:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:22:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:23:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:24:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:25:46 + | +LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:28:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:29:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:30:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:31:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:32:46 + | +LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_fetch_update.rs:35:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_fetch_update.rs:36:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_fetch_update.rs:39:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_fetch_update.rs:40:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_fetch_update.rs:43:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:44:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 16 previous errors + From 159178e5d4a023f3945b504b49d3742b40453fee Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:32:38 -0700 Subject: [PATCH 0577/1110] Separate compare_exchange and compare_exchange_weak uitests --- tests/ui/atomic_ordering_exchange.rs | 39 ------ tests/ui/atomic_ordering_exchange.stderr | 130 +---------------- tests/ui/atomic_ordering_exchange_weak.rs | 47 +++++++ tests/ui/atomic_ordering_exchange_weak.stderr | 131 ++++++++++++++++++ 4 files changed, 179 insertions(+), 168 deletions(-) create mode 100644 tests/ui/atomic_ordering_exchange_weak.rs create mode 100644 tests/ui/atomic_ordering_exchange_weak.stderr diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs index 2f60a98f037..1ddc12f9ab2 100644 --- a/tests/ui/atomic_ordering_exchange.rs +++ b/tests/ui/atomic_ordering_exchange.rs @@ -42,43 +42,4 @@ fn main() { // Acquire/AcqRel forbids failure order of SeqCst let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); - - // compare_exchange_weak tests - - // Allowed ordering combos - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::SeqCst); - - // AcqRel is always forbidden as a failure ordering - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); - - // Release is always forbidden as a failure ordering - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); - - // Release success order forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); - - // Relaxed success order also forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); - - // Acquire/AcqRel forbids failure order of SeqCst - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); } diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr index 26ec15be518..c09d2d6ab21 100644 --- a/tests/ui/atomic_ordering_exchange.stderr +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -127,133 +127,5 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:60:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:61:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:62:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:63:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:64:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:67:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:68:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:69:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:70:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:71:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange.rs:74:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange.rs:75:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange.rs:78:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange.rs:79:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` - --> $DIR/atomic_ordering_exchange.rs:82:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:83:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: aborting due to 32 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/atomic_ordering_exchange_weak.rs b/tests/ui/atomic_ordering_exchange_weak.rs new file mode 100644 index 00000000000..59069902507 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange_weak.rs @@ -0,0 +1,47 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicPtr, Ordering}; + +fn main() { + let ptr = &mut 5; + let ptr2 = &mut 10; + // `compare_exchange_weak` testing + let x = AtomicPtr::new(ptr); + + // Allowed ordering combos + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); +} diff --git a/tests/ui/atomic_ordering_exchange_weak.stderr b/tests/ui/atomic_ordering_exchange_weak.stderr new file mode 100644 index 00000000000..7210431bd1b --- /dev/null +++ b/tests/ui/atomic_ordering_exchange_weak.stderr @@ -0,0 +1,131 @@ +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:23:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:24:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:25:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:26:66 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:27:66 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:30:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:31:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:32:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:33:66 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:34:66 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange_weak.rs:37:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange_weak.rs:38:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange_weak.rs:41:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange_weak.rs:42:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange_weak.rs:45:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:46:66 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 16 previous errors + From b65745545f410b31c5ecdd33a8299a65da578af2 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:40:43 -0700 Subject: [PATCH 0578/1110] Use AtomicU8 in ordering example so all operations can be demonstrated --- clippy_lints/src/atomic_ordering.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index ff2c281ec9d..d4e070b0c24 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -18,22 +18,22 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,no_run - /// # use std::sync::atomic::{self, AtomicBool, Ordering}; + /// # use std::sync::atomic::{self, AtomicU8, Ordering}; /// - /// let x = AtomicBool::new(true); + /// let x = AtomicU8::new(0); /// /// let _ = x.load(Ordering::Release); /// let _ = x.load(Ordering::AcqRel); /// - /// x.store(false, Ordering::Acquire); - /// x.store(false, Ordering::AcqRel); + /// x.store(1, Ordering::Acquire); + /// x.store(2, Ordering::AcqRel); /// /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); /// - /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); - /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); - /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val ^ val)); + /// let _ = x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::SeqCst); + /// let _ = x.compare_exchange_weak(2, 3, Ordering::SeqCst, Ordering::Release); + /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val + val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, From 4b5326b0d62104801f0b33c4d8f3749d93eebc02 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 23:12:57 -0700 Subject: [PATCH 0579/1110] Address small review comments --- clippy_lints/src/atomic_ordering.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index d4e070b0c24..614e33a06da 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -139,6 +139,7 @@ fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option None } } + fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; @@ -197,7 +198,7 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { INVALID_ATOMIC_ORDERING, failure_order_arg.span, &format!( - "{}'s failure ordering may not stronger than the success ordering of `{}`", + "{}'s failure ordering may not be stronger than the success ordering of `{}`", method, success_ord_name, ), From 3a072131da2e574e914073af6d72360ead735781 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 23:22:01 -0700 Subject: [PATCH 0580/1110] Ah, right, rerun the scripts --- tests/ui/atomic_ordering_exchange.stderr | 12 ++++++------ tests/ui/atomic_ordering_exchange_weak.stderr | 12 ++++++------ tests/ui/atomic_ordering_fetch_update.stderr | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr index c09d2d6ab21..4b9bfef7974 100644 --- a/tests/ui/atomic_ordering_exchange.stderr +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -79,7 +79,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); | = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange.rs:35:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); @@ -87,7 +87,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange.rs:36:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); @@ -95,7 +95,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange.rs:39:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); @@ -103,7 +103,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange.rs:40:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); @@ -111,7 +111,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Acquire` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire` --> $DIR/atomic_ordering_exchange.rs:43:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); @@ -119,7 +119,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `AcqRel` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel` --> $DIR/atomic_ordering_exchange.rs:44:56 | LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); diff --git a/tests/ui/atomic_ordering_exchange_weak.stderr b/tests/ui/atomic_ordering_exchange_weak.stderr index 7210431bd1b..de7026f3ffa 100644 --- a/tests/ui/atomic_ordering_exchange_weak.stderr +++ b/tests/ui/atomic_ordering_exchange_weak.stderr @@ -79,7 +79,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering:: | = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange_weak.rs:37:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); @@ -87,7 +87,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange_weak.rs:38:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); @@ -95,7 +95,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange_weak.rs:41:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); @@ -103,7 +103,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange_weak.rs:42:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); @@ -111,7 +111,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire` --> $DIR/atomic_ordering_exchange_weak.rs:45:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); @@ -119,7 +119,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering: | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel` --> $DIR/atomic_ordering_exchange_weak.rs:46:66 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr index 362e104a244..694548ece97 100644 --- a/tests/ui/atomic_ordering_fetch_update.stderr +++ b/tests/ui/atomic_ordering_fetch_update.stderr @@ -79,7 +79,7 @@ LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some( | = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Release` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_fetch_update.rs:35:47 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); @@ -87,7 +87,7 @@ LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Release` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_fetch_update.rs:36:47 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); @@ -95,7 +95,7 @@ LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some( | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_fetch_update.rs:39:47 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); @@ -103,7 +103,7 @@ LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some( | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_fetch_update.rs:40:47 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); @@ -111,7 +111,7 @@ LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Acquire` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire` --> $DIR/atomic_ordering_fetch_update.rs:43:47 | LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); @@ -119,7 +119,7 @@ LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some( | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `AcqRel` +error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel` --> $DIR/atomic_ordering_fetch_update.rs:44:46 | LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); From f3489d4a5ed944b3f62238c13cda6097dfbed0c8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 9 Sep 2020 11:04:29 +1200 Subject: [PATCH 0581/1110] fix some use of `snippet` in `types.rs` --- clippy_lints/src/types.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6c6188d61ad..b6d405cca77 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -321,14 +321,15 @@ impl Types { if let Some(def_id) = res.opt_def_id() { if Some(def_id) == cx.tcx.lang_items().owned_box() { if let Some(span) = match_borrows_parameter(cx, qpath) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Box<&T>`", "try", - snippet(cx, span, "..").to_string(), - Applicability::MachineApplicable, + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, ); return; // don't recurse into the type } @@ -345,14 +346,15 @@ impl Types { } } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), - Applicability::MachineApplicable, + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, ); return; // don't recurse into the type } @@ -368,26 +370,31 @@ impl Types { GenericArg::Type(ty) => ty.span, _ => return, }; + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - format!("Rc<{}>", snippet(cx, inner_span, "..")), - Applicability::MachineApplicable, + format!( + "Rc<{}>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + applicability, ); return; // don't recurse into the type } if let Some(span) = match_borrows_parameter(cx, qpath) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc<&T>`", "try", - snippet(cx, span, "..").to_string(), - Applicability::MachineApplicable, + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, ); return; // don't recurse into the type } @@ -546,7 +553,6 @@ impl Types { // details. return; } - let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, BORROWED_BOX, @@ -556,8 +562,12 @@ impl Types { format!( "&{}{}", ltopt, - &snippet_with_applicability(cx, inner.span, "..", &mut applicability) + &snippet(cx, inner.span, "..") ), + // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item + // because the trait impls of it will break otherwise; + // and there may be other cases that result in invalid code. + // For example, type coercion doesn't work nicely. Applicability::Unspecified, ); return; // don't recurse into the type From 2d56512580919483268c3e3bf2e028c8614805f2 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Sep 2020 14:18:05 +0200 Subject: [PATCH 0582/1110] Cleanup of rustup --- clippy_lints/src/temporary_assignment.rs | 9 +++------ tests/ui/temporary_assignment.rs | 6 ------ tests/ui/temporary_assignment.stderr | 8 ++++---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 2b6ddadd4c1..fb891866364 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -21,11 +21,8 @@ declare_clippy_lint! { "assignments to temporaries" } -fn is_temporary(_cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Struct(..) | ExprKind::Tup(..) => true, - _ => false, - } +fn is_temporary(expr: &Expr<'_>) -> bool { + matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..)) } declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]); @@ -37,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment { while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { base = f; } - if is_temporary(cx, base) && !is_adjusted(cx, base) { + if is_temporary(base) && !is_adjusted(cx, base) { span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary"); } } diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index d6f56d40c5d..ac4c1bc6597 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; @@ -54,11 +53,6 @@ fn main() { ArrayStruct { array: [0] }.array[0] = 1; (0, 0).0 = 1; - A.0 = 2; - B.field = 2; - C.structure.field = 2; - D.array[0] = 2; - // no error s.field = 1; t.0 = 1; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f05..7d79901a28d 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ From f1775f05b7a20c83dd018bc716e1eb5578cfceb0 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 10 Sep 2020 22:39:08 +0900 Subject: [PATCH 0583/1110] Fix typo --- clippy_lints/src/await_holding_lock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index f18e7e5d997..367534499fd 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -10,7 +10,7 @@ declare_clippy_lint! { /// **What it does:** Checks for calls to await while holding a /// non-async-aware MutexGuard. /// - /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot + /// **Why is this bad?** The Mutex types found in std::sync and parking_lot /// are not designed to operate in an async context across await points. /// /// There are two potential solutions. One is to use an asynx-aware Mutex From 36a864854f41dfd25c45fa4881812bd005bd5054 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 10 Sep 2020 17:08:10 +0200 Subject: [PATCH 0584/1110] Fix spelling of "Known problems section" of `interior_mutable_key` --- clippy_lints/src/mut_key.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index fa3a99dad9d..0826ad0ab55 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -12,10 +12,10 @@ declare_clippy_lint! { /// `BtreeSet` rely on either the hash or the order of keys be unchanging, /// so having types with interior mutability is a bad idea. /// - /// **Known problems:** It's correct to use a struct, that contains interior mutability, - /// as a key; when its `Hash` implementation doesn't access any these interior mutable types. - /// However, this lint is unable to recognise it so cause a false positive. - /// `bytes` ctate is a great example of this. + /// **Known problems:** It's correct to use a struct, that contains interior mutability + /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types. + /// However, this lint is unable to recognize this, so it causes a false positive in theses cases. + /// The `bytes` crate is a great example of this. /// /// **Example:** /// ```rust From 1778a1ec4615a42a8ba9497006b07859186c08a1 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Sep 2020 22:17:31 +0900 Subject: [PATCH 0585/1110] Restrict `same_item_push` to suppress false positives It emits a lint when the pushed item is a literal, a constant and an immutable binding that are initialized with those. --- clippy_lints/src/loops.rs | 118 ++++++++++++++++++++++++++------- tests/ui/same_item_push.rs | 38 +++++++++++ tests/ui/same_item_push.stderr | 34 ++++++---- 3 files changed, 154 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8352a8a3d2c..f417e3a0caf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -826,7 +826,7 @@ struct FixedOffsetVar<'hir> { } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind { + let is_slice = match ty.kind() { ty::Ref(_, subty, _) => is_slice_like(cx, subty), ty::Slice(..) | ty::Array(..) => true, _ => false, @@ -1003,7 +1003,7 @@ fn detect_manual_memcpy<'tcx>( start: Some(start), end: Some(end), limits, - }) = higher::range(cx, arg) + }) = higher::range(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { @@ -1140,23 +1140,95 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - // Make sure that the push does not involve possibly mutating values - if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values if let PatKind::Wild = pat.kind { let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + if let ExprKind::Path(ref qpath) = pushed_item.kind { + match qpath_res(cx, qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + if_chain! { + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + }, + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ), + _ => {}, + } + } else if let ExprKind::Lit(..) = pushed_item.kind { + // literal + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } } } @@ -1177,7 +1249,7 @@ fn check_for_loop_range<'tcx>( start: Some(start), ref end, limits, - }) = higher::range(cx, arg) + }) = higher::range(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { @@ -1355,7 +1427,7 @@ fn is_end_eq_array_len<'tcx>( if_chain! { if let ExprKind::Lit(ref lit) = end.kind; if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind; + if let ty::Array(_, arr_len_const) = indexed_ty.kind(); if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); then { return match limits { @@ -1592,7 +1664,7 @@ fn check_for_loop_over_map_kv<'tcx>( if let PatKind::Tuple(ref pat, _) = pat.kind { if pat.len() == 2 { let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match cx.typeck_results().expr_ty(arg).kind { + let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl), (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not), @@ -1679,7 +1751,7 @@ fn check_for_mut_range_bound(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<' start: Some(start), end: Some(end), .. - }) = higher::range(cx, arg) + }) = higher::range(arg) { let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; if mut_ids[0].is_some() || mut_ids[1].is_some() { @@ -1920,7 +1992,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = ty.kind { + if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -1932,7 +2004,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = ty.kind { + if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -2030,7 +2102,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc - match ty.kind { + match ty.kind() { ty::Array(_, n) => n .try_eval_usize(cx.tcx, cx.param_env) .map_or(false, |val| (0..=32).contains(&val)), diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index ff1088f86f6..bd4792c4a76 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -1,5 +1,7 @@ #![warn(clippy::same_item_push)] +const VALUE: u8 = 7; + fn mutate_increment(x: &mut u8) -> u8 { *x += 1; *x @@ -86,4 +88,40 @@ fn main() { for a in vec_a { vec12.push(2u8.pow(a.kind)); } + + // Fix #5902 + let mut vec13: Vec = Vec::new(); + let mut item = 0; + for _ in 0..10 { + vec13.push(item); + item += 10; + } + + // Fix #5979 + let mut vec14: Vec = Vec::new(); + for _ in 0..10 { + vec14.push(std::fs::File::open("foobar").unwrap()); + } + // Fix #5979 + #[derive(Clone)] + struct S {} + + trait T {} + impl T for S {} + + let mut vec15: Vec> = Vec::new(); + for _ in 0..10 { + vec15.push(Box::new(S {})); + } + + let mut vec16 = Vec::new(); + for _ in 0..20 { + vec16.push(VALUE); + } + + let mut vec17 = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec17.push(item); + } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index ddc5d48cd41..4896479791a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,22 +1,14 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:16:9 - | -LL | spaces.push(vec![b' ']); - | ^^^^^^ - | - = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) - -error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:22:9 + --> $DIR/same_item_push.rs:24:9 | LL | vec2.push(item); | ^^^^ | + = note: `-D clippy::same-item-push` implied by `-D warnings` = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:28:9 + --> $DIR/same_item_push.rs:30:9 | LL | vec3.push(item); | ^^^^ @@ -24,12 +16,28 @@ LL | vec3.push(item); = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:33:9 + --> $DIR/same_item_push.rs:35:9 | LL | vec4.push(13); | ^^^^ | = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) -error: aborting due to 4 previous errors +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:119:9 + | +LL | vec16.push(VALUE); + | ^^^^^ + | + = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:125:9 + | +LL | vec17.push(item); + | ^^^^^ + | + = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + +error: aborting due to 5 previous errors From 0117ea2b016133145f9e02e27421ac3672b42f57 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:25:31 +0900 Subject: [PATCH 0586/1110] Refactoring: use inner function --- clippy_lints/src/loops.rs | 69 ++++++++++++--------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f417e3a0caf..c27acdd2236 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1150,8 +1150,6 @@ fn detect_same_item_push<'tcx>( { // Make sure that the push does not involve possibly mutating values if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant @@ -1167,33 +1165,11 @@ fn detect_same_item_push<'tcx>( then { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } _ => {}, @@ -1202,37 +1178,34 @@ fn detect_same_item_push<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } } else if let ExprKind::Lit(..) = pushed_item.kind { // literal - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } } } } + + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } /// Checks for looping over a range and then indexing a sequence with it. From b80576fba633e1fc214c9f6900d7ca3424bda6d0 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:34:51 +0900 Subject: [PATCH 0587/1110] Some refactoring --- clippy_lints/src/loops.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c27acdd2236..c59185bd7bd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,10 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + if !matches!(pat.kind, PatKind::Wild) { + return; + } + // Determine whether it is safe to lint the body let mut same_item_push_visitor = SameItemPushVisitor { should_lint: true, @@ -1149,8 +1153,8 @@ fn detect_same_item_push<'tcx>( .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - if let ExprKind::Path(ref qpath) = pushed_item.kind { + match pushed_item.kind { + ExprKind::Path(ref qpath) => { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant Res::Local(hir_id) => { @@ -1161,7 +1165,7 @@ fn detect_same_item_push<'tcx>( if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); let parent_node = cx.tcx.hir().get_parent_node(hir_id); if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + if let Some(init) = parent_let_expr.init; then { match init.kind { // immutable bindings that are initialized with literal @@ -1181,10 +1185,9 @@ fn detect_same_item_push<'tcx>( Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } - } else if let ExprKind::Lit(..) = pushed_item.kind { - // literal - emit_lint(cx, vec, pushed_item); - } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, } } } From 14faebe20ea82392f393c3ff5efaab7250e51989 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 22:45:27 +0900 Subject: [PATCH 0588/1110] Add some tests to `same_item_push` Add tests in which the variable is initialized with a match expression and function call --- tests/ui/same_item_push.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bd4792c4a76..7ca829854db 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -11,6 +11,10 @@ fn increment(x: u8) -> u8 { x + 1 } +fn fun() -> usize { + 42 +} + fn main() { // Test for basic case let mut spaces = Vec::with_capacity(10); @@ -124,4 +128,21 @@ fn main() { for _ in 0..20 { vec17.push(item); } + + let mut vec18 = Vec::new(); + let item = 42; + let item = fun(); + for _ in 0..20 { + vec18.push(item); + } + + let mut vec19 = Vec::new(); + let key = 1; + for _ in 0..20 { + let item = match key { + 1 => 10, + _ => 0, + }; + vec19.push(item); + } } From 2972ad3ef661071531a61ec8999b668a6b734b74 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:03:15 +0900 Subject: [PATCH 0589/1110] Refactoring: tests in `same_item_push` --- tests/ui/same_item_push.rs | 111 +++++++++++++++++---------------- tests/ui/same_item_push.stderr | 40 ++++++------ 2 files changed, 77 insertions(+), 74 deletions(-) diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 7ca829854db..a37c8782ec3 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -16,64 +16,76 @@ fn fun() -> usize { } fn main() { - // Test for basic case + // ** linted cases ** + let mut vec: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + vec.push(13); + } + + let mut vec = Vec::new(); + for _ in 0..20 { + vec.push(VALUE); + } + + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + } + + // ** non-linted cases ** let mut spaces = Vec::with_capacity(10); for _ in 0..10 { spaces.push(vec![b' ']); } - let mut vec2: Vec = Vec::new(); - let item = 2; - for _ in 5..=20 { - vec2.push(item); - } - - let mut vec3: Vec = Vec::new(); - for _ in 0..15 { - let item = 2; - vec3.push(item); - } - - let mut vec4: Vec = Vec::new(); - for _ in 0..15 { - vec4.push(13); - } - // Suggestion should not be given as pushed variable can mutate - let mut vec5: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; for _ in 0..30 { - vec5.push(mutate_increment(&mut item)); + vec.push(mutate_increment(&mut item)); } - let mut vec6: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; let mut item2 = &mut mutate_increment(&mut item); for _ in 0..30 { - vec6.push(mutate_increment(item2)); + vec.push(mutate_increment(item2)); } - let mut vec7: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { - vec7.push(a); + vec.push(a); } - let mut vec8: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec8.push(increment(i)); + vec.push(increment(i)); } - let mut vec9: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec9.push(i + i * i); + vec.push(i + i * i); } // Suggestion should not be given as there are multiple pushes that are not the same - let mut vec10: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let item: u8 = 2; for _ in 0..30 { - vec10.push(item); - vec10.push(item * 2); + vec.push(item); + vec.push(item * 2); } // Suggestion should not be given as Vec is not involved @@ -88,23 +100,23 @@ fn main() { for i in 0..30 { vec_a.push(A { kind: i }); } - let mut vec12: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for a in vec_a { - vec12.push(2u8.pow(a.kind)); + vec.push(2u8.pow(a.kind)); } // Fix #5902 - let mut vec13: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item = 0; for _ in 0..10 { - vec13.push(item); + vec.push(item); item += 10; } // Fix #5979 - let mut vec14: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for _ in 0..10 { - vec14.push(std::fs::File::open("foobar").unwrap()); + vec.push(std::fs::File::open("foobar").unwrap()); } // Fix #5979 #[derive(Clone)] @@ -113,36 +125,27 @@ fn main() { trait T {} impl T for S {} - let mut vec15: Vec> = Vec::new(); + let mut vec: Vec> = Vec::new(); for _ in 0..10 { - vec15.push(Box::new(S {})); + vec.push(Box::new(S {})); } - let mut vec16 = Vec::new(); - for _ in 0..20 { - vec16.push(VALUE); - } - - let mut vec17 = Vec::new(); - let item = VALUE; - for _ in 0..20 { - vec17.push(item); - } - - let mut vec18 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let item = 42; let item = fun(); for _ in 0..20 { - vec18.push(item); + vec.push(item); } - let mut vec19 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let key = 1; for _ in 0..20 { let item = match key { 1 => 10, _ => 0, }; - vec19.push(item); + vec.push(item); } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 4896479791a..d9ffa15780a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,43 +1,43 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:24:9 + --> $DIR/same_item_push.rs:23:9 | -LL | vec2.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:30:9 + --> $DIR/same_item_push.rs:29:9 | -LL | vec3.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:35:9 + --> $DIR/same_item_push.rs:34:9 | -LL | vec4.push(13); - | ^^^^ +LL | vec.push(13); + | ^^^ | - = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:119:9 + --> $DIR/same_item_push.rs:39:9 | -LL | vec16.push(VALUE); - | ^^^^^ +LL | vec.push(VALUE); + | ^^^ | - = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:125:9 + --> $DIR/same_item_push.rs:45:9 | -LL | vec17.push(item); - | ^^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: aborting due to 5 previous errors From 730ca457f580247667ed0cd5965bc08752ebc0b3 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:12:04 +0900 Subject: [PATCH 0590/1110] Address `items_after_statement` --- clippy_lints/src/loops.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c59185bd7bd..6c54c07869a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,23 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + if !matches!(pat.kind, PatKind::Wild) { return; } @@ -1192,23 +1209,6 @@ fn detect_same_item_push<'tcx>( } } } - - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } } /// Checks for looping over a range and then indexing a sequence with it. From 09f7a377a663043c6f63ded70436ac0969e4abc7 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Thu, 10 Sep 2020 14:50:10 -0700 Subject: [PATCH 0591/1110] Add comments to the invalid_atomic_ordering example --- clippy_lints/src/atomic_ordering.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 614e33a06da..703d8a6f62b 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -22,18 +22,27 @@ declare_clippy_lint! { /// /// let x = AtomicU8::new(0); /// + /// // Bad: `Release` and `AcqRel` cannot be used for `load`. /// let _ = x.load(Ordering::Release); /// let _ = x.load(Ordering::AcqRel); /// + /// // Bad: `Acquire` and `AcqRel` cannot be used for `store`. /// x.store(1, Ordering::Acquire); /// x.store(2, Ordering::AcqRel); /// + /// // Bad: `Relaxed` cannot be used as a fence's ordering. /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); /// - /// let _ = x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::SeqCst); - /// let _ = x.compare_exchange_weak(2, 3, Ordering::SeqCst, Ordering::Release); - /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val + val)); + /// // Bad: `Release` and `AcqRel` are both always invalid + /// // for the failure ordering (the last arg). + /// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release); + /// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel); + /// + /// // Bad: The failure ordering is not allowed to be + /// // stronger than the success order, and `SeqCst` is + /// // stronger than `Relaxed`. + /// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, From 2487f8f461134f93459415642f730ac6c4a2d659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 11 Sep 2020 16:52:25 +0200 Subject: [PATCH 0592/1110] into_iter_on_ref: rephrase lint message: will not move the x -> will not consume the x imo that's a bit clearer. --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/into_iter_on_ref.stderr | 54 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ba69c8266b1..98e027b6d22 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3374,7 +3374,7 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_ INTO_ITER_ON_REF, method_span, &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`", + "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", method_name, kind, ), "call directly", diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 1cd6400b019..28003b365bb 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,4 +1,4 @@ -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() @@ -6,157 +6,157 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() | = note: `-D clippy::into-iter-on-ref` implied by `-D warnings` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:15:46 | LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:16:41 | LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:17:44 | LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:19:32 | LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:20:36 | LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:21:40 | LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option` --> $DIR/into_iter_on_ref.rs:23:24 | LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option` --> $DIR/into_iter_on_ref.rs:24:28 | LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result` --> $DIR/into_iter_on_ref.rs:25:32 | LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result` --> $DIR/into_iter_on_ref.rs:26:37 | LL | let _ = (&mut Err::(7)).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:27:34 | LL | let _ = (&Vec::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:28:38 | LL | let _ = (&mut Vec::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap` --> $DIR/into_iter_on_ref.rs:29:44 | LL | let _ = (&BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap` --> $DIR/into_iter_on_ref.rs:30:48 | LL | let _ = (&mut BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque` --> $DIR/into_iter_on_ref.rs:31:39 | LL | let _ = (&VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque` --> $DIR/into_iter_on_ref.rs:32:43 | LL | let _ = (&mut VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList` --> $DIR/into_iter_on_ref.rs:33:41 | LL | let _ = (&LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList` --> $DIR/into_iter_on_ref.rs:34:45 | LL | let _ = (&mut LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap` --> $DIR/into_iter_on_ref.rs:35:43 | LL | let _ = (&HashMap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap` --> $DIR/into_iter_on_ref.rs:36:47 | LL | let _ = (&mut HashMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet` --> $DIR/into_iter_on_ref.rs:38:39 | LL | let _ = (&BTreeSet::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap` --> $DIR/into_iter_on_ref.rs:39:41 | LL | let _ = (&BinaryHeap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet` --> $DIR/into_iter_on_ref.rs:40:38 | LL | let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path` --> $DIR/into_iter_on_ref.rs:41:43 | LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf` --> $DIR/into_iter_on_ref.rs:42:47 | LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:44:26 | LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() From 7ba1a8fec42ca11c169bcff3650f9c1e108b6743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 13 Sep 2020 12:42:14 +0200 Subject: [PATCH 0593/1110] useless_conversion: show type in error message. changelog: useless_conversion: show type in error message. --- clippy_lints/src/useless_conversion.rs | 10 +++++----- tests/ui/useless_conversion.stderr | 22 +++++++++++----------- tests/ui/useless_conversion_try.stderr | 18 +++++++++--------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 615440e15f3..4e4a206a583 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), None, "consider removing `.try_into()`", ); @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), None, &hint, ); @@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), &sugg_msg, sugg.to_string(), Applicability::MachineApplicable, // snippet diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index f1e880d2696..11c6efb25cc 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,61 +10,61 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: useless conversion to the same type +error: useless conversion to the same type: `i32` --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: useless conversion to the same type +error: useless conversion to the same type: `std::str::Lines` --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: useless conversion to the same type +error: useless conversion to the same type: `i32` --> $DIR/useless_conversion.rs:71:13 | LL | let _ = i32::from(a + b) * 3; diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index b765727c168..2e0d9129bfb 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,4 +1,4 @@ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion_try.rs:6:13 | LL | let _ = T::try_from(val).unwrap(); @@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider removing `T::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion_try.rs:7:5 | LL | val.try_into().unwrap() @@ -19,7 +19,7 @@ LL | val.try_into().unwrap() | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:29:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); @@ -27,7 +27,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); @@ -35,7 +35,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | = help: consider removing `TryFrom::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); @@ -43,7 +43,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); | = help: consider removing `String::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); @@ -51,7 +51,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | = help: consider removing `String::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:33:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); @@ -59,7 +59,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:34:21 | LL | let _: String = "".to_owned().try_into().unwrap(); @@ -67,7 +67,7 @@ LL | let _: String = "".to_owned().try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:35:27 | LL | let _: String = match String::from("_").try_into() { From 9ff7e5d98413d10dd74e3b9509c7a58f6dd6818b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 13 Sep 2020 23:23:16 +0900 Subject: [PATCH 0594/1110] Downgrade `verbose_bit_mask` to pedantic --- clippy_lints/src/bit_mask.rs | 2 +- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- tests/ui/trailing_zeros.rs | 1 + tests/ui/trailing_zeros.stderr | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 81a34021e8a..a4ee54076ee 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -90,7 +90,7 @@ declare_clippy_lint! { /// if x & 0b1111 == 0 { } /// ``` pub VERBOSE_BIT_MASK, - style, + pedantic, "expressions where a bit mask is less readable than the corresponding method call" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1795fd10fa1..c017c5cb5d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1157,6 +1157,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), @@ -1254,7 +1255,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::USELESS_ATTRIBUTE), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&booleans::LOGIC_BUG), @@ -1512,7 +1512,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), - LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 04d486438b1..a7d38c93433 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2637,7 +2637,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "verbose_bit_mask", - group: "style", + group: "pedantic", desc: "expressions where a bit mask is less readable than the corresponding method call", deprecation: None, module: "bit_mask", diff --git a/tests/ui/trailing_zeros.rs b/tests/ui/trailing_zeros.rs index 1cef8c2cfc9..fbdc977b769 100644 --- a/tests/ui/trailing_zeros.rs +++ b/tests/ui/trailing_zeros.rs @@ -1,4 +1,5 @@ #![allow(unused_parens)] +#![warn(clippy::verbose_bit_mask)] fn main() { let x: i32 = 42; diff --git a/tests/ui/trailing_zeros.stderr b/tests/ui/trailing_zeros.stderr index 320d9cc3f64..79855111830 100644 --- a/tests/ui/trailing_zeros.stderr +++ b/tests/ui/trailing_zeros.stderr @@ -1,5 +1,5 @@ error: bit mask could be simplified with a call to `trailing_zeros` - --> $DIR/trailing_zeros.rs:5:13 + --> $DIR/trailing_zeros.rs:6:13 | LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4` @@ -7,7 +7,7 @@ LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` error: bit mask could be simplified with a call to `trailing_zeros` - --> $DIR/trailing_zeros.rs:6:13 + --> $DIR/trailing_zeros.rs:7:13 | LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5` From d1f0f04a488d027fdf91e08cdf25df00fb677205 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 14 Sep 2020 06:11:35 +0200 Subject: [PATCH 0595/1110] New lint: `manual-strip` Add a new lint, `manual-strip`, that suggests using the `str::strip_prefix` and `str::strip_suffix` methods introduced in Rust 1.45 when the same functionality is performed 'manually'. Closes #5734 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_strip.rs | 246 +++++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 3 + src/lintlist/mod.rs | 7 + tests/ui/manual_strip.rs | 59 ++++++++ tests/ui/manual_strip.stderr | 132 +++++++++++++++++ 7 files changed, 453 insertions(+) create mode 100644 clippy_lints/src/manual_strip.rs create mode 100644 tests/ui/manual_strip.rs create mode 100644 tests/ui/manual_strip.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 285a2ff8060..a6fafdf5357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1672,6 +1672,7 @@ Released 2018-09-13 [`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_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c017c5cb5d0..38ddc69c8cb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod macro_use; mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; +mod manual_strip; mod map_clone; mod map_identity; mod map_unit_fn; @@ -626,6 +627,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &manual_strip::MANUAL_STRIP, &map_clone::MAP_CLONE, &map_identity::MAP_IDENTITY, &map_unit_fn::OPTION_MAP_UNIT_FN, @@ -1109,6 +1111,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_late_pass(|| box manual_strip::ManualStrip); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1335,6 +1338,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1626,6 +1630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), + LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs new file mode 100644 index 00000000000..127938aecd6 --- /dev/null +++ b/clippy_lints/src/manual_strip.rs @@ -0,0 +1,246 @@ +use crate::consts::{constant, Constant}; +use crate::utils::usage::mutated_variables; +use crate::utils::{ + eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, +}; + +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::BinOpKind; +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using + /// the pattern's length. + /// + /// **Why is this bad?** + /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no + /// slicing which may panic and the compiler does not need to insert this panic code. It is + /// also sometimes more readable as it removes the need for duplicating or storing the pattern + /// used by `str::{starts,ends}_with` and in the slicing. + /// + /// **Known problems:** + /// None. + /// + /// **Example:** + /// + /// ```rust + /// let s = "hello, world!"; + /// if s.starts_with("hello, ") { + /// assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + /// } + /// ``` + /// Use instead: + /// ```rust + /// let s = "hello, world!"; + /// if let Some(end) = s.strip_prefix("hello, ") { + /// assert_eq!(end.to_uppercase(), "WORLD!"); + /// } + /// ``` + pub MANUAL_STRIP, + complexity, + "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing" +} + +declare_lint_pass!(ManualStrip => [MANUAL_STRIP]); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum StripKind { + Prefix, + Suffix, +} + +impl<'tcx> LateLintPass<'tcx> for ManualStrip { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let Some((cond, then, _)) = higher::if_block(&expr); + if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id); + if let ExprKind::Path(target_path) = &target_arg.kind; + then { + let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { + StripKind::Prefix + } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { + StripKind::Suffix + } else { + return; + }; + let target_res = qpath_res(cx, &target_path, target_arg.hir_id); + if target_res == Res::Err { + return; + }; + + if_chain! { + if let Res::Local(hir_id) = target_res; + if let Some(used_mutably) = mutated_variables(then, cx); + if used_mutably.contains(&hir_id); + then { + return; + } + } + + let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); + if !strippings.is_empty() { + + let kind_word = match strip_kind { + StripKind::Prefix => "prefix", + StripKind::Suffix => "suffix", + }; + + let test_span = expr.span.until(then.span); + span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| { + diag.span_note(test_span, &format!("the {} was tested here", kind_word)); + multispan_sugg( + diag, + &format!("try using the `strip_{}` method", kind_word), + vec![(test_span, + format!("if let Some() = {}.strip_{}({}) ", + snippet(cx, target_arg.span, ".."), + kind_word, + snippet(cx, pattern.span, "..")))] + .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), + ) + }); + } + } + } + } +} + +// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. +fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, method_def_id, &paths::STR_LEN); + then { + Some(arg) + } + else { + None + } + } +} + +// Returns the length of the `expr` if it's a constant string or char. +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + let (value, _) = constant(cx, cx.typeck_results(), expr)?; + match value { + Constant::Str(value) => Some(value.len() as u128), + Constant::Char(value) => Some(value.len_utf8() as u128), + _ => None, + } +} + +// Tests if `expr` equals the length of the pattern. +fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Lit(Spanned { + node: LitKind::Int(n, _), + .. + }) = expr.kind + { + constant_length(cx, pattern).map_or(false, |length| length == n) + } else { + len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg)) + } +} + +// Tests if `expr` is a `&str`. +fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.typeck_results().expr_ty_adjusted(&expr).kind() { + ty::Ref(_, ty, _) => ty.is_str(), + _ => false, + } +} + +// Removes the outer `AddrOf` expression if needed. +fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> { + if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind { + unref + } else { + expr + } +} + +// Find expressions where `target` is stripped using the length of `pattern`. +// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` +// method. +fn find_stripping<'tcx>( + cx: &LateContext<'tcx>, + strip_kind: StripKind, + target: Res, + pattern: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) -> Vec { + struct StrippingFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + strip_kind: StripKind, + target: Res, + pattern: &'tcx Expr<'tcx>, + results: Vec, + } + + impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + if_chain! { + if is_ref_str(self.cx, ex); + let unref = peel_ref(ex); + if let ExprKind::Index(indexed, index) = &unref.kind; + if let Some(range) = higher::range(index); + if let higher::Range { start, end, .. } = range; + if let ExprKind::Path(path) = &indexed.kind; + if qpath_res(self.cx, path, ex.hir_id) == self.target; + then { + match (self.strip_kind, start, end) { + (StripKind::Prefix, Some(start), None) => { + if eq_pattern_length(self.cx, self.pattern, start) { + self.results.push(ex.span); + return; + } + }, + (StripKind::Suffix, None, Some(end)) => { + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; + if let Some(left_arg) = len_arg(self.cx, left); + if let ExprKind::Path(left_path) = &left_arg.kind; + if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target; + if eq_pattern_length(self.cx, self.pattern, right); + then { + self.results.push(ex.span); + return; + } + } + }, + _ => {} + } + } + } + + walk_expr(self, ex); + } + } + + let mut finder = StrippingFinder { + cx, + strip_kind, + target, + pattern, + results: vec![], + }; + walk_expr(&mut finder, expr); + finder.results +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 65320d6a0e0..f0f7719e2fd 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -115,6 +115,9 @@ pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; +pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7d38c93433..8bceef80abf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1144,6 +1144,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "manual_strip", + group: "complexity", + desc: "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing", + deprecation: None, + module: "manual_strip", + }, Lint { name: "manual_swap", group: "complexity", diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs new file mode 100644 index 00000000000..d1b4772c7de --- /dev/null +++ b/tests/ui/manual_strip.rs @@ -0,0 +1,59 @@ +#![warn(clippy::manual_strip)] + +fn main() { + let s = "abc"; + + if s.starts_with("ab") { + str::to_string(&s["ab".len()..]); + s["ab".len()..].to_string(); + + str::to_string(&s[2..]); + s[2..].to_string(); + } + + if s.ends_with("bc") { + str::to_string(&s[..s.len() - "bc".len()]); + s[..s.len() - "bc".len()].to_string(); + + str::to_string(&s[..s.len() - 2]); + s[..s.len() - 2].to_string(); + } + + // Character patterns + if s.starts_with('a') { + str::to_string(&s[1..]); + s[1..].to_string(); + } + + // Variable prefix + let prefix = "ab"; + if s.starts_with(prefix) { + str::to_string(&s[prefix.len()..]); + } + + // Constant prefix + const PREFIX: &str = "ab"; + if s.starts_with(PREFIX) { + str::to_string(&s[PREFIX.len()..]); + str::to_string(&s[2..]); + } + + // Constant target + const TARGET: &str = "abc"; + if TARGET.starts_with(prefix) { + str::to_string(&TARGET[prefix.len()..]); + } + + // String target - not mutated. + let s1: String = "abc".into(); + if s1.starts_with("ab") { + s1[2..].to_uppercase(); + } + + // String target - mutated. (Don't lint.) + let mut s2: String = "abc".into(); + if s2.starts_with("ab") { + s2.push('d'); + s2[2..].to_uppercase(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr new file mode 100644 index 00000000000..1352a8713d4 --- /dev/null +++ b/tests/ui/manual_strip.stderr @@ -0,0 +1,132 @@ +error: stripping a prefix manually + --> $DIR/manual_strip.rs:7:24 + | +LL | str::to_string(&s["ab".len()..]); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/manual_strip.rs:6:5 + | +LL | if s.starts_with("ab") { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("ab") { +LL | str::to_string(); +LL | .to_string(); +LL | +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a suffix manually + --> $DIR/manual_strip.rs:15:24 + | +LL | str::to_string(&s[..s.len() - "bc".len()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the suffix was tested here + --> $DIR/manual_strip.rs:14:5 + | +LL | if s.ends_with("bc") { + | ^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_suffix` method + | +LL | if let Some() = s.strip_suffix("bc") { +LL | str::to_string(); +LL | .to_string(); +LL | +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:24:24 + | +LL | str::to_string(&s[1..]); + | ^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:23:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix('a') { +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:31:24 + | +LL | str::to_string(&s[prefix.len()..]); + | ^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:30:5 + | +LL | if s.starts_with(prefix) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix(prefix) { +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:37:24 + | +LL | str::to_string(&s[PREFIX.len()..]); + | ^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:36:5 + | +LL | if s.starts_with(PREFIX) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix(PREFIX) { +LL | str::to_string(); +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:44:24 + | +LL | str::to_string(&TARGET[prefix.len()..]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:43:5 + | +LL | if TARGET.starts_with(prefix) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = TARGET.strip_prefix(prefix) { +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:50:9 + | +LL | s1[2..].to_uppercase(); + | ^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:49:5 + | +LL | if s1.starts_with("ab") { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s1.strip_prefix("ab") { +LL | .to_uppercase(); + | + +error: aborting due to 7 previous errors + From 15244a88df5cfd475df010ad945474c658749192 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 14 Sep 2020 06:11:35 +0200 Subject: [PATCH 0596/1110] Fix `manual-strip` dogfood errors --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/loops.rs | 8 +++----- clippy_lints/src/misc_early.rs | 6 +++--- clippy_lints/src/redundant_clone.rs | 5 ++--- tests/ui/let_if_seq.rs | 1 + tests/ui/let_if_seq.stderr | 8 ++++---- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 50121a054c7..62bb70af06e 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -534,7 +534,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { return false; } - let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s }; + let s = s.strip_suffix('s').unwrap_or(s); s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6c54c07869a..8f5675a61b9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2601,11 +2601,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont span, NEEDLESS_COLLECT_MSG, |diag| { - let (arg, pred) = if contains_arg.starts_with('&') { - ("x", &contains_arg[1..]) - } else { - ("&x", &*contains_arg) - }; + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); diag.span_suggestion( span, "replace with", diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 02789735c17..9cb1cfb915d 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -377,8 +377,8 @@ impl EarlyLintPass for MiscEarlyLints { if let PatKind::Ident(_, ident, None) = arg.pat.kind { let arg_name = ident.to_string(); - if arg_name.starts_with('_') { - if let Some(correspondence) = registered_names.get(&arg_name[1..]) { + if let Some(arg_name) = arg_name.strip_prefix('_') { + if let Some(correspondence) = registered_names.get(arg_name) { span_lint( cx, DUPLICATE_UNDERSCORE_ARGUMENT, @@ -386,7 +386,7 @@ impl EarlyLintPass for MiscEarlyLints { &format!( "`{}` already exists, having another argument having almost the same \ name makes code comprehension and documentation more difficult", - arg_name[1..].to_owned() + arg_name ), ); } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 57a45e628db..1a7f36fbdad 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -239,10 +239,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { ); let mut app = Applicability::MaybeIncorrect; - let mut call_snip = &snip[dot + 1..]; + let call_snip = &snip[dot + 1..]; // Machine applicable when `call_snip` looks like `foobar()` - if call_snip.ends_with("()") { - call_snip = call_snip[..call_snip.len()-2].trim(); + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { app = Applicability::MachineApplicable; } diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 802beeb4be6..32a67f181df 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -33,6 +33,7 @@ fn issue985_alt() -> i32 { x } +#[allow(clippy::manual_strip)] fn issue975() -> String { let mut udn = "dummy".to_string(); if udn.starts_with("uuid:") { diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index c53a63a541b..7de560c7348 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:63:5 + --> $DIR/let_if_seq.rs:64:5 | LL | / let mut foo = 0; LL | | if f() { @@ -11,7 +11,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:68:5 + --> $DIR/let_if_seq.rs:69:5 | LL | / let mut bar = 0; LL | | if f() { @@ -25,7 +25,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:76:5 + --> $DIR/let_if_seq.rs:77:5 | LL | / let quz; LL | | if f() { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:105:5 + --> $DIR/let_if_seq.rs:106:5 | LL | / let mut baz = 0; LL | | if f() { From 1b5317f68b2b55803d5051e9945f9a33817fccef Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Sun, 13 Sep 2020 21:52:25 -0600 Subject: [PATCH 0597/1110] Add rc_buffer lint for Rc and other buffer types --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/types.rs | 84 ++++++++++++++++++++++++++++++++- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 +++ tests/ui/rc_buffer.rs | 13 +++++ tests/ui/rc_buffer.stderr | 28 +++++++++++ 7 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/ui/rc_buffer.rs create mode 100644 tests/ui/rc_buffer.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 285a2ff8060..8922f5e7027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1775,6 +1775,7 @@ Released 2018-09-13 [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c017c5cb5d0..239eeb10bb4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -837,6 +837,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, &types::UNIT_ARG, @@ -1804,6 +1805,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(&transmute::USELESS_TRANSMUTE), + LintId::of(&types::RC_BUFFER), LintId::of(&use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b6d405cca77..5f3a2e0b6d4 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -215,11 +215,41 @@ declare_clippy_lint! { "redundant allocation" } +declare_clippy_lint! { + /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// + /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since + /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// + /// While mutating a buffer type would still be possible with Rc::get_mut(), it only + /// works if there are no additional references yet, which defeats the purpose of + /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner + /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// be used. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// # use std::rc::Rc; + /// fn foo(interned: Rc) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// fn foo(interned: Rc) { ... } + /// ``` + pub RC_BUFFER, + nursery, + "shared ownership of a buffer type" +} + pub struct Types { vec_box_size_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { @@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str]) None } +fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { + if match_type_parameter(cx, qpath, &paths::STRING).is_some() { + return Some("str"); + } + if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() { + return Some("std::ffi::OsStr"); + } + if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() { + return Some("std::path::Path"); + } + None +} + fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); if_chain! { @@ -385,6 +428,45 @@ impl Types { ); return; // don't recurse into the type } + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc` when T is a buffer type", + "try", + format!("Rc<{}>", alternate), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc` when T is a buffer type", + "try", + format!( + "Rc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } if let Some(span) = match_borrows_parameter(cx, qpath) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 65320d6a0e0..2df11d2efcf 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -113,6 +113,7 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; +pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7d38c93433..d0c6a1d63d9 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1851,6 +1851,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "ranges", }, + Lint { + name: "rc_buffer", + group: "nursery", + desc: "shared ownership of a buffer type", + deprecation: None, + module: "types", + }, Lint { name: "redundant_allocation", group: "perf", diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs new file mode 100644 index 00000000000..c8c2bec67ee --- /dev/null +++ b/tests/ui/rc_buffer.rs @@ -0,0 +1,13 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; + +#[warn(clippy::rc_buffer)] +struct S { + a: Rc, + b: Rc, + c: Rc>, + d: Rc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr new file mode 100644 index 00000000000..641a13a2251 --- /dev/null +++ b/tests/ui/rc_buffer.stderr @@ -0,0 +1,28 @@ +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:7:8 + | +LL | a: Rc, + | ^^^^^^^^^^ help: try: `Rc` + | + = note: `-D clippy::rc-buffer` implied by `-D warnings` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:8:8 + | +LL | b: Rc, + | ^^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:9:8 + | +LL | c: Rc>, + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:10:8 + | +LL | d: Rc, + | ^^^^^^^^^^^^ help: try: `Rc` + +error: aborting due to 4 previous errors + From 2dd7175d60e070c7ee2b4609bdb17eae16e381f0 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Sun, 13 Sep 2020 22:17:10 -0600 Subject: [PATCH 0598/1110] Apply rc_buffer lint to Arc --- clippy_lints/src/types.rs | 40 +++++++++++++++++++++++++++++++++++ tests/ui/rc_buffer_arc.rs | 13 ++++++++++++ tests/ui/rc_buffer_arc.stderr | 28 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 tests/ui/rc_buffer_arc.rs create mode 100644 tests/ui/rc_buffer_arc.stderr diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5f3a2e0b6d4..da04d07885b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -480,6 +480,46 @@ impl Types { ); return; // don't recurse into the type } + } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc` when T is a buffer type", + "try", + format!("Arc<{}>", alternate), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc` when T is a buffer type", + "try", + format!( + "Arc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) { if_chain! { // Get the _ part of Vec<_> diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs new file mode 100644 index 00000000000..a878b0ab336 --- /dev/null +++ b/tests/ui/rc_buffer_arc.rs @@ -0,0 +1,13 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::sync::Arc; + +#[warn(clippy::rc_buffer)] +struct S { + a: Arc, + b: Arc, + c: Arc>, + d: Arc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr new file mode 100644 index 00000000000..c4b01621046 --- /dev/null +++ b/tests/ui/rc_buffer_arc.stderr @@ -0,0 +1,28 @@ +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:7:8 + | +LL | a: Arc, + | ^^^^^^^^^^^ help: try: `Arc` + | + = note: `-D clippy::rc-buffer` implied by `-D warnings` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:8:8 + | +LL | b: Arc, + | ^^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:9:8 + | +LL | c: Arc>, + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:10:8 + | +LL | d: Arc, + | ^^^^^^^^^^^^^ help: try: `Arc` + +error: aborting due to 4 previous errors + From 4d73ccaa9419b393b9c94f977ec0e158897feeb3 Mon Sep 17 00:00:00 2001 From: Haraman Johal Date: Tue, 15 Sep 2020 00:20:31 +0100 Subject: [PATCH 0599/1110] clarify margin of error in wording of float comparison operator lint messages --- clippy_lints/src/misc.rs | 6 +++--- tests/ui/float_cmp.stderr | 22 +++++++++++----------- tests/ui/float_cmp_const.stderr | 30 +++++++++++++++--------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index d4a50dd9013..81e6214f143 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -411,16 +411,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if !is_comparing_arrays { diag.span_suggestion( expr.span, - "consider comparing them within some error", + "consider comparing them within some margin of error", format!( - "({}).abs() {} error", + "({}).abs() {} error_margin", lhs - rhs, if op == BinOpKind::Eq { '<' } else { '>' } ), Applicability::HasPlaceholders, // snippet ); } - diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`"); + diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index 2d454e8e70d..f7c380fc915 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -2,34 +2,34 @@ error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:65:5 | LL | ONE as f64 != 2.0; - | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error` + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` | = note: `-D clippy::float-cmp` implied by `-D warnings` - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:70:5 | LL | x == 1.0; - | ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:73:5 | LL | twice(x) != twice(ONE as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:93:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays --> $DIR/float_cmp.rs:98:5 @@ -37,15 +37,15 @@ error: strict comparison of `f32` or `f64` arrays LL | a1 == a2; | ^^^^^^^^ | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:99:5 | LL | a1[0] == a2[0]; - | ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error` + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 6 previous errors diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr index 19dc4a284b7..5d0455363e8 100644 --- a/tests/ui/float_cmp_const.stderr +++ b/tests/ui/float_cmp_const.stderr @@ -2,58 +2,58 @@ error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:20:5 | LL | 1f32 == ONE; - | ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error` + | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` | = note: `-D clippy::float-cmp-const` implied by `-D warnings` - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:21:5 | LL | TWO == ONE; - | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error` + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:22:5 | LL | TWO != ONE; - | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error` + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:23:5 | LL | ONE + ONE == TWO; - | ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error` + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:25:5 | LL | x as f32 == ONE; - | ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error` + | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:28:5 | LL | v == ONE; - | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:29:5 | LL | v != ONE; - | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant arrays --> $DIR/float_cmp_const.rs:61:5 @@ -61,7 +61,7 @@ error: strict comparison of `f32` or `f64` constant arrays LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 8 previous errors From fd151f5135def6ffae7530f17ff0c458239f209d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 15 Sep 2020 08:51:52 +0900 Subject: [PATCH 0600/1110] Remove an extra blank line in `shadow_same` --- clippy_lints/src/shadow.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 087d50c90e6..ef4a54735a4 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -25,7 +25,6 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let x = 1; - /// /// // Bad /// let x = &x; /// From 8afa7ed6aec37c5423cffe12dbc854c954799fb3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:32:48 +0200 Subject: [PATCH 0601/1110] Add internal lint MatchTypeOnDiagItem --- clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 115 ++++++++++++++++++++++- clippy_lints/src/utils/mod.rs | 5 + tests/ui/match_type_on_diag_item.rs | 50 ++++++++++ tests/ui/match_type_on_diag_item.stderr | 33 +++++++ 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 tests/ui/match_type_on_diag_item.rs create mode 100644 tests/ui/match_type_on_diag_item.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 44afd7e82fe..b60a80e07d7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -867,6 +867,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, @@ -1112,6 +1113,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1240,6 +1242,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(&utils::internal_lints::PRODUCE_ICE), ]); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 8fa5d22210a..5beb4be5b29 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,6 +1,6 @@ use crate::utils::{ - is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, + is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, + snippet, span_lint, span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; @@ -206,6 +206,29 @@ declare_clippy_lint! { "found collapsible `span_lint_and_then` calls" } +declare_clippy_lint! { + /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item + /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// + /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Good: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type)) + /// ``` + pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + internal, + "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -652,3 +675,89 @@ fn suggest_note( Applicability::MachineApplicable, ); } + +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); + +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) { + return; + } + + if_chain! { + // Check if this is a call to utils::match_type() + if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; + if let ExprKind::Path(fn_qpath) = &fn_path.kind; + if match_qpath(&fn_qpath, &["utils", "match_type"]); + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, ty_path); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id()); + // Check if the matched type is a diagnostic item + let diag_items = cx.tcx.diagnostic_items(ty_did.krate); + if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); + then { + let cx_snippet = snippet(cx, context.span, "_"); + let ty_snippet = snippet(cx, ty.span, "_"); + + span_lint_and_sugg( + cx, + MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + expr.span, + "usage of `utils::match_type() on a type diagnostic item`", + "try", + format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), + Applicability::MaybeIncorrect, + ); + } + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + use rustc_hir::ItemKind; + + match &expr.kind { + ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), + ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { + if let Some(init) = local.init { + return path_to_matched_type(cx, init); + } + } + }, + Res::Def(DefKind::Const | DefKind::Static, def_id) => { + if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { + if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { + let body = cx.tcx.hir().body(body_id); + return path_to_matched_type(cx, &body.value); + } + } + }, + _ => {}, + }, + ExprKind::Array(exprs) => { + let segments: Vec = exprs + .iter() + .filter_map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some(sym.as_str()); + } + } + + None + }) + .collect(); + + if segments.len() == exprs.len() { + return Some(segments); + } + }, + _ => {}, + } + + None +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3ebbfed6456..8db7e693e62 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -130,6 +130,9 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { } /// Checks if type is struct, enum or union type with the given def path. +/// +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { match ty.kind() { ty::Adt(adt, _) => match_def_path(cx, adt.did, path), @@ -138,6 +141,8 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } /// Checks if the type is equal to a diagnostic item +/// +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), diff --git a/tests/ui/match_type_on_diag_item.rs b/tests/ui/match_type_on_diag_item.rs new file mode 100644 index 00000000000..fe950b0aa7c --- /dev/null +++ b/tests/ui/match_type_on_diag_item.rs @@ -0,0 +1,50 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; + +mod paths { + pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; +} + +mod utils { + use super::*; + + pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { + false + } +} + +use utils::match_type; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +static OPTION: [&str; 3] = ["core", "option", "Option"]; + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let ty = cx.typeck_results().expr_ty(expr); + + let _ = match_type(cx, ty, &paths::VEC); + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + let rc_path = &["alloc", "rc", "Rc"]; + let _ = utils::match_type(cx, ty, rc_path); + } +} + +fn main() {} diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr new file mode 100644 index 00000000000..c89137eb758 --- /dev/null +++ b/tests/ui/match_type_on_diag_item.stderr @@ -0,0 +1,33 @@ +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:41:17 + | +LL | let _ = match_type(cx, ty, &paths::VEC); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | +note: the lint level is defined here + --> $DIR/match_type_on_diag_item.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:42:17 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:43:17 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:46:17 + | +LL | let _ = utils::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))` + +error: aborting due to 4 previous errors + From 332c2dcb4dfe5151d7c8d44cdf96c4abd06db593 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:48:04 +0200 Subject: [PATCH 0602/1110] Fix dogfood after MatchTypeOnDiagItem --- clippy_lints/src/loops.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6c54c07869a..3d2fd0eee85 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1114,7 +1114,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type)); if path.ident.name.as_str() == "push"; then { return Some((self_expr, pushed_item)) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 98e027b6d22..de966cccd11 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1808,7 +1808,7 @@ fn lint_or_fun_call<'tcx>( _ => (), } - if match_type(cx, ty, &paths::VEC) { + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { return; } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9494efe736c..60e5e7bfed3 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,6 +1,6 @@ use crate::utils; use crate::utils::sugg::Sugg; -use crate::utils::{match_type, paths, span_lint_and_sugg}; +use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -73,7 +73,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" - && match_type(cx, &cx.typeck_results().expr_ty(&receiver), &paths::RESULT) + && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type)) } else { false } From c86a7e5f38e54f4404cc71874316eb0b4dba200b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:57:24 +0200 Subject: [PATCH 0603/1110] Misc doc updates --- clippy_lints/src/utils/diagnostics.rs | 9 +++++++++ doc/common_tools_writing_lints.md | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index e4e65b5f4d4..0a58231558e 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -51,6 +51,8 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// The `note` message is presented separately from the main lint message /// and is attached to a specific span: /// +/// If you change the signature, remember to update the internal lint `CollapsibleCalls` +/// /// # Example /// /// ```ignore @@ -126,6 +130,7 @@ pub fn span_lint_and_note<'a, T: LintContext>( /// Like `span_lint` but allows to add notes, help and suggestions using a closure. /// /// If you need to customize your lint output a lot, use this function. +/// If you change the signature, remember to update the internal lint `CollapsibleCalls` pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) where F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), @@ -168,6 +173,10 @@ pub fn span_lint_hir_and_then( /// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x > /// 2)"`. /// +/// If you change the signature, remember to update the internal lint `CollapsibleCalls` +/// +/// # Example +/// /// ```ignore /// error: This `.fold` can be more succinctly expressed as `.any` /// --> $DIR/methods.rs:390:13 diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 9dd4c8a5f7a..53c3d084dbc 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -60,7 +60,7 @@ impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, _args) = &expr.kind; + if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind; // Check the name of this method is `some_method` if path.ident.name == sym!(some_method); then { From d0b5663d30457d1a9ec4f98eb9baff7123b77172 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 15 Sep 2020 10:31:50 +0200 Subject: [PATCH 0604/1110] Fix usage of backquotes in suggestion --- clippy_lints/src/utils/internal_lints.rs | 2 +- tests/ui/match_type_on_diag_item.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 5beb4be5b29..f201494a024 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -704,7 +704,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.span, - "usage of `utils::match_type() on a type diagnostic item`", + "usage of `utils::match_type()` on a type diagnostic item", "try", format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr index c89137eb758..5e5fe9e3a3e 100644 --- a/tests/ui/match_type_on_diag_item.stderr +++ b/tests/ui/match_type_on_diag_item.stderr @@ -1,4 +1,4 @@ -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); @@ -11,19 +11,19 @@ LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:43:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:46:17 | LL | let _ = utils::match_type(cx, ty, rc_path); From 44eb66d947191be51f0a7b7d35c05936a4142a97 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 16 Sep 2020 00:25:00 +0900 Subject: [PATCH 0605/1110] Add note to `shadow_unrelated` This lint can be disabled at function level. --- clippy_lints/src/shadow.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ef4a54735a4..225fe58906f 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -74,7 +74,9 @@ declare_clippy_lint! { /// names to bindings or introducing more scopes to contain the bindings. /// /// **Known problems:** This lint, as the other shadowing related lints, - /// currently only catches very simple patterns. + /// currently only catches very simple patterns. Note that + /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level + /// for this lint. /// /// **Example:** /// ```rust From 16b6cebaa67655a793d90d296c03e2c5496ec4ce Mon Sep 17 00:00:00 2001 From: Haraman Johal Date: Tue, 15 Sep 2020 17:29:41 +0100 Subject: [PATCH 0606/1110] update lint docs --- clippy_lints/src/misc.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 81e6214f143..67a3685fd0d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -99,11 +99,11 @@ declare_clippy_lint! { /// if y != x {} // where both are floats /// /// // Good - /// let error = f64::EPSILON; // Use an epsilon for comparison + /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. - /// // let error = std::f64::EPSILON; - /// if (y - 1.23f64).abs() < error { } - /// if (y - x).abs() > error { } + /// // let error_margin = std::f64::EPSILON; + /// if (y - 1.23f64).abs() < error_margin { } + /// if (y - x).abs() > error_margin { } /// ``` pub FLOAT_CMP, correctness, @@ -242,10 +242,10 @@ declare_clippy_lint! { /// if x == ONE { } // where both are floats /// /// // Good - /// let error = f64::EPSILON; // Use an epsilon for comparison + /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. - /// // let error = std::f64::EPSILON; - /// if (x - ONE).abs() < error { } + /// // let error_margin = std::f64::EPSILON; + /// if (x - ONE).abs() < error_margin { } /// ``` pub FLOAT_CMP_CONST, restriction, From ecbe9ac0e9b68b171e4a48db40e91e2e44370c2a Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 15 Sep 2020 21:30:01 +0200 Subject: [PATCH 0607/1110] manual-strip: Add additional test --- tests/ui/manual_strip.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index d1b4772c7de..cbb84eb5c7e 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -56,4 +56,11 @@ fn main() { s2.push('d'); s2[2..].to_uppercase(); } + + // Target not stripped. (Don't lint.) + let s3 = String::from("abcd"); + let s4 = String::from("efgh"); + if s3.starts_with("ab") { + s4[2..].to_string(); + } } From 79a0e5110a0168945d43e334e6dc0d12e0bc1487 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 15 Sep 2020 21:25:03 +0200 Subject: [PATCH 0608/1110] manual-strip: Fix formatting --- clippy_lints/src/manual_strip.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 127938aecd6..4afb0ab3bad 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -124,8 +124,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E if match_def_path(cx, method_def_id, &paths::STR_LEN); then { Some(arg) - } - else { + } else { None } } From 6ba36bcfd3802d9520d0ac48dabfe6dc06d8dc82 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 22 Aug 2020 00:43:04 +0200 Subject: [PATCH 0609/1110] option_if_let_else - distinguish pure from impure else expressions --- .../src/methods/bind_instead_of_map.rs | 21 ++- clippy_lints/src/methods/mod.rs | 66 +++------ .../src/methods/unnecessary_lazy_eval.rs | 76 ++--------- clippy_lints/src/option_if_let_else.rs | 33 ++--- clippy_lints/src/utils/eager_or_lazy.rs | 128 ++++++++++++++++++ clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/sugg.rs | 2 +- clippy_lints/src/utils/usage.rs | 66 +++++++++ tests/ui/option_if_let_else.fixed | 10 ++ tests/ui/option_if_let_else.rs | 15 ++ tests/ui/option_if_let_else.stderr | 36 +++-- tests/ui/unnecessary_lazy_eval.fixed | 42 +++--- tests/ui/unnecessary_lazy_eval.rs | 26 ++-- tests/ui/unnecessary_lazy_eval.stderr | 112 ++++++++++----- 14 files changed, 420 insertions(+), 214 deletions(-) create mode 100644 clippy_lints/src/utils/eager_or_lazy.rs diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 498f12518f8..ae37942e55a 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -12,6 +12,7 @@ use rustc_middle::hir::map::Map; use rustc_span::Span; pub(crate) struct OptionAndThenSome; + impl BindInsteadOfMap for OptionAndThenSome { const TYPE_NAME: &'static str = "Option"; const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; @@ -24,6 +25,7 @@ impl BindInsteadOfMap for OptionAndThenSome { } pub(crate) struct ResultAndThenOk; + impl BindInsteadOfMap for ResultAndThenOk { const TYPE_NAME: &'static str = "Result"; const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; @@ -36,6 +38,7 @@ impl BindInsteadOfMap for ResultAndThenOk { } pub(crate) struct ResultOrElseErrInfo; + impl BindInsteadOfMap for ResultOrElseErrInfo { const TYPE_NAME: &'static str = "Result"; const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; @@ -120,9 +123,9 @@ pub(crate) trait BindInsteadOfMap { } } - fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); - let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if_chain! { if !in_macro(ret_expr.span); if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; @@ -153,12 +156,13 @@ pub(crate) trait BindInsteadOfMap { ) }); } + can_sugg } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) { - return; + return false; } match args[1].kind { @@ -166,8 +170,10 @@ pub(crate) trait BindInsteadOfMap { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); - if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { - Self::lint_closure(cx, expr, closure_expr); + if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + true + } else { + Self::lint_closure(cx, expr, closure_expr) } }, // `_.and_then(Some)` case, which is no-op. @@ -181,8 +187,9 @@ pub(crate) trait BindInsteadOfMap { snippet(cx, args[0].span, "..").into(), Applicability::MachineApplicable, ); + true }, - _ => {}, + _ => false, } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 98e027b6d22..914f9f94e77 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -25,14 +25,15 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; +use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, - last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, - method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, + is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, + match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, + single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, + walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1454,18 +1455,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); - bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); - bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and"); + } }, ["or_else", ..] => { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); - bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or"); + } }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), @@ -1508,9 +1512,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), - ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), - ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), - ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), _ => {}, } @@ -1714,37 +1718,6 @@ fn lint_or_fun_call<'tcx>( name: &str, args: &'tcx [hir::Expr<'_>], ) { - // Searches an expression for method calls or function calls that aren't ctors - struct FunCallFinder<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: bool, - } - - impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - let call_found = match &expr.kind { - // ignore enum and struct constructors - hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), - hir::ExprKind::MethodCall(..) => true, - _ => false, - }; - - if call_found { - self.found |= true; - } - - if !self.found { - intravisit::walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - } - /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. fn check_unwrap_or_default( cx: &LateContext<'_>, @@ -1825,8 +1798,7 @@ fn lint_or_fun_call<'tcx>( if_chain! { if know_types.iter().any(|k| k.2.contains(&name)); - let mut finder = FunCallFinder { cx: &cx, found: false }; - if { finder.visit_expr(&arg); finder.found }; + if is_lazyness_candidate(cx, arg); if !contains_return(&arg); let self_ty = cx.typeck_results().expr_ty(self_expr); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 31517659c34..08b3eab9b7c 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,78 +1,17 @@ -use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; -use if_chain::if_chain; +use crate::utils::{eager_or_lazy, usage}; +use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use super::UNNECESSARY_LAZY_EVALUATIONS; -// Return true if the expression is an accessor of any of the arguments -fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { - params.iter().any(|arg| { - if_chain! { - if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; - if let [p, ..] = path.segments; - then { - ident.name == p.ident.name - } else { - false - } - } - }) -} - -fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { - paths.iter().any(|candidate| match_qpath(path, candidate)) -} - -fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { - match expr.kind { - // Closures returning literals can be unconditionally simplified - hir::ExprKind::Lit(_) => true, - - hir::ExprKind::Index(ref object, ref index) => { - // arguments are not being indexed into - if expr_uses_argument(object, params) { - false - } else { - // arguments are not used as index - !expr_uses_argument(index, params) - } - }, - - // Reading fields can be simplified if the object is not an argument of the closure - hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), - - // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), - - // Calls to Some, Ok, Err can be considered literals if they don't derive an argument - hir::ExprKind::Call(ref func, ref args) => if_chain! { - if variant_calls; // Disable lint when rules conflict with bind_instead_of_map - if let hir::ExprKind::Path(ref path) = func.kind; - if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); - then { - // Recursively check all arguments - args.iter().all(|arg| can_simplify(arg, params, variant_calls)) - } else { - false - } - }, - - // For anything more complex than the above, a closure is probably the right solution, - // or the case is handled by an other lint - _ => false, - } -} - /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` pub(super) fn lint<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], - allow_variant_calls: bool, simplify_using: &str, ) { let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); @@ -81,10 +20,13 @@ pub(super) fn lint<'tcx>( if is_option || is_result { if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; + let body_expr = &body.value; - if can_simplify(ex, params, allow_variant_calls) { + if usage::BindingUsageFinder::are_params_used(cx, body) { + return; + } + + if eager_or_lazy::is_eagerness_candidate(cx, body_expr) { let msg = if is_option { "unnecessary closure used to substitute value for `Option::None`" } else { @@ -101,7 +43,7 @@ pub(super) fn lint<'tcx>( "{0}.{1}({2})", snippet(cx, args[0].span, ".."), simplify_using, - snippet(cx, ex.span, ".."), + snippet(cx, body_expr.span, ".."), ), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9494efe736c..5e2652b48cb 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,4 +1,5 @@ use crate::utils; +use crate::utils::eager_or_lazy; use crate::utils::sugg::Sugg; use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; @@ -13,22 +14,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more - /// idiomatically done with `Option::map_or` (if the else bit is a simple - /// expression) or `Option::map_or_else` (if the else bit is a longer - /// block). + /// idiomatically done with `Option::map_or` (if the else bit is a pure + /// expression) or `Option::map_or_else` (if the else bit is an impure + /// expresion). /// /// **Why is this bad?** /// Using the dedicated functions of the Option type is clearer and /// more concise than an if let expression. /// /// **Known problems:** - /// This lint uses whether the block is just an expression or if it has - /// more statements to decide whether to use `Option::map_or` or - /// `Option::map_or_else`. If you have a single expression which calls - /// an expensive function, then it would be more efficient to use - /// `Option::map_or_else`, but this lint would suggest `Option::map_or`. - /// - /// Also, this lint uses a deliberately conservative metric for checking + /// This lint uses a deliberately conservative metric for checking /// if the inside of either body contains breaks or continues which will /// cause it to not suggest a fix if either block contains a loop with /// continues or breaks contained within the loop. @@ -92,6 +87,7 @@ struct OptionIfLetElseOccurence { struct ReturnBreakContinueMacroVisitor { seen_return_break_continue: bool, } + impl ReturnBreakContinueMacroVisitor { fn new() -> ReturnBreakContinueMacroVisitor { ReturnBreakContinueMacroVisitor { @@ -99,6 +95,7 @@ impl ReturnBreakContinueMacroVisitor { } } } + impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -157,7 +154,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { } /// If this is the else body of an if/else expression, then we need to wrap -/// it in curcly braces. Otherwise, we don't. +/// it in curly braces. Otherwise, we don't. fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if let Some(Expr { @@ -199,7 +196,10 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo /// If this expression is the option if let/else construct we're detecting, then /// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn detect_option_if_let_else<'tcx>( + cx: &'_ LateContext<'tcx>, + expr: &'_ Expr<'tcx>, +) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; @@ -214,10 +214,7 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option "map_or_else", - _ => "map_or", - }; + let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); let wrap_braces = should_wrap_in_braces(cx, expr); let (as_ref, as_mut) = match &cond_expr.kind { @@ -243,8 +240,8 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option LateLintPass<'a> for OptionIfLetElse { - fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(detection) = detect_option_if_let_else(cx, expr) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs new file mode 100644 index 00000000000..6938d9971d9 --- /dev/null +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -0,0 +1,128 @@ +//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa. +//! +//! Things to consider: +//! - has the expression side-effects? +//! - is the expression computationally expensive? +//! +//! See lints: +//! - unnecessary-lazy-evaluations +//! - or-fun-call +//! - option-if-let-else + +use crate::utils::is_ctor_or_promotable_const_function; +use rustc_hir::def::{DefKind, Res}; + +use rustc_hir::intravisit; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; + +use rustc_hir::{Block, Expr, ExprKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +/// Is the expr pure (is it free from side-effects)? +/// This function is named so to stress that it isn't exhaustive and returns FNs. +fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true, + ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr), + ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)), + ExprKind::Struct(_, fields, expr) => { + fields.iter().all(|f| identify_some_pure_patterns(f.expr)) + && expr.map_or(true, |e| identify_some_pure_patterns(e)) + }, + ExprKind::Call( + &Expr { + kind: + ExprKind::Path(QPath::Resolved( + _, + Path { + res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..), + .. + }, + )), + .. + }, + args, + ) => args.iter().all(|expr| identify_some_pure_patterns(expr)), + ExprKind::Block( + &Block { + stmts, + expr: Some(expr), + .. + }, + _, + ) => stmts.is_empty() && identify_some_pure_patterns(expr), + ExprKind::Box(..) + | ExprKind::Array(..) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Index(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) + | ExprKind::Err => false, + } +} + +/// Identify some potentially computationally expensive patterns. +/// This function is named so to stress that its implementation is non-exhaustive. +/// It returns FNs and FPs. +fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + // Searches an expression for method calls or function calls that aren't ctors + struct FunCallFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: bool, + } + + impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + let call_found = match &expr.kind { + // ignore enum and struct constructors + ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + ExprKind::MethodCall(..) => true, + _ => false, + }; + + if call_found { + self.found |= true; + } + + if !self.found { + intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + } + + let mut finder = FunCallFinder { cx, found: false }; + finder.visit_expr(expr); + finder.found +} + +pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr) +} + +pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + identify_some_potentially_expensive_patterns(cx, expr) +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3ebbfed6456..7efeef62690 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -10,6 +10,7 @@ pub mod comparisons; pub mod conf; pub mod constants; mod diagnostics; +pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod inspector; diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 811fde388d1..ec8b7e59b59 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -49,7 +49,7 @@ impl<'a> Sugg<'a> { /// Convenience function around `hir_opt` for suggestions with a default /// text. pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { - Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default))) + Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default))) } /// Same as `hir`, but it adapts the applicability level by following rules: diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 4a64b935ac9..ea1dc3be29b 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,6 +1,8 @@ use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; @@ -108,3 +110,67 @@ pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool { walk_expr(&mut visitor, body); !visitor.used } + +pub struct ParamBindingIdCollector { + binding_hir_ids: Vec, +} +impl<'tcx> ParamBindingIdCollector { + fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_body(body); + finder.binding_hir_ids + } +} +impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { + type Map = Map<'tcx>; + + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + self.binding_hir_ids.push(hir_id); + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +pub struct BindingUsageFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + binding_ids: Vec, + usage_found: bool, +} +impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { + pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool { + let mut finder = BindingUsageFinder { + cx, + binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body), + usage_found: false, + }; + finder.visit_body(body); + finder.usage_found + } +} +impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if !self.usage_found { + intravisit::walk_expr(self, expr); + } + } + + fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { + if let hir::def::Res::Local(id) = path.res { + if self.binding_ids.contains(&id) { + self.usage_found = true; + } + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 695a460cc4e..a7fb00a2705 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::option_if_let_else)] +#![allow(clippy::redundant_closure)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) @@ -36,6 +37,14 @@ fn longer_body(arg: Option) -> u32 { }) } +fn impure_else(arg: Option) { + let side_effect = || { + println!("return 1"); + 1 + }; + let _ = arg.map_or_else(|| side_effect(), |x| x); +} + fn test_map_or_else(arg: Option) { let _ = arg.map_or_else(|| { let mut y = 1; @@ -71,4 +80,5 @@ fn main() { let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); + let _ = impure_else(None); } diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index dee80d26bd9..895fd86321f 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::option_if_let_else)] +#![allow(clippy::redundant_closure)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { @@ -52,6 +53,19 @@ fn longer_body(arg: Option) -> u32 { } } +fn impure_else(arg: Option) { + let side_effect = || { + println!("return 1"); + 1 + }; + let _ = if let Some(x) = arg { + x + } else { + // map_or_else must be suggested + side_effect() + }; +} + fn test_map_or_else(arg: Option) { let _ = if let Some(x) = arg { x * x * x * x @@ -89,4 +103,5 @@ fn main() { let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); + let _ = impure_else(None); } diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 7005850efaf..b69fe767682 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:5:5 + --> $DIR/option_if_let_else.rs:6:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:15:12 + --> $DIR/option_if_let_else.rs:16:12 | LL | } else if let Some(x) = string { | ____________^ @@ -22,19 +22,19 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:23:13 + --> $DIR/option_if_let_else.rs:24:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:26:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -54,13 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:31:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:33:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -80,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:38:13 + --> $DIR/option_if_let_else.rs:39:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -100,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:47:5 + --> $DIR/option_if_let_else.rs:48:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -119,7 +119,19 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:56:13 + --> $DIR/option_if_let_else.rs:61:13 + | +LL | let _ = if let Some(x) = arg { + | _____________^ +LL | | x +LL | | } else { +LL | | // map_or_else must be suggested +LL | | side_effect() +LL | | }; + | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:70:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -142,10 +154,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:85:13 + --> $DIR/option_if_let_else.rs:99:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index fa66e68794e..4980c111499 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -2,6 +2,7 @@ #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] +#![allow(clippy::map_identity)] struct Deep(Option); @@ -34,13 +35,13 @@ fn main() { let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); - let _ = opt.unwrap_or(ext_arr[0]); + let _ = opt.unwrap_or_else(|| ext_arr[0]); let _ = opt.and(ext_opt); let _ = opt.or(ext_opt); let _ = opt.or(None); let _ = opt.get_or_insert(2); let _ = opt.ok_or(2); - let _ = opt.ok_or(ext_arr[0]); + let _ = nested_tuple_opt.unwrap_or(Some((1, 2))); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or(2); @@ -60,7 +61,6 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = nested_opt.unwrap_or_else(|| Some(some_call())); - let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); @@ -69,13 +69,16 @@ fn main() { let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); + let _ = opt.ok_or_else(|| ext_arr[0]); - // These are handled by bind_instead_of_map + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); - let _: Option = None.or_else(|| Some(3)); - let _ = deep.0.or_else(|| Some(3)); - let _ = opt.or_else(|| Some(3)); + + // should lint, bind_instead_of_map doesn't apply + let _: Option = None.or(Some(3)); + let _ = deep.0.or(Some(3)); + let _ = opt.or(Some(3)); // Should lint - Result let res: Result = Err(5); @@ -92,26 +95,27 @@ fn main() { let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + // should not lint, bind_instead_of_map takes priority let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); - - let _: Result = res.or_else(|err| Ok(err)); let _: Result = res.or_else(|err| Err(err)); - // These are handled by bind_instead_of_map let _: Result = res.and_then(|_| Ok(2)); let _: Result = res.and_then(|_| Ok(astronomers_pi)); let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); - - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); let _: Result = res.or_else(|_| Err(astronomers_pi)); let _: Result = res.or_else(|_| Err(ext_str.some_field)); + + // should lint, bind_instead_of_map doesn't apply + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); + + let _: Result = res.and(Err(2)); + let _: Result = res.and(Err(astronomers_pi)); + let _: Result = res.and(Err(ext_str.some_field)); + + let _: Result = res.or(Ok(2)); + let _: Result = res.or(Ok(astronomers_pi)); + let _: Result = res.or(Ok(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 04f47d1aa29..0b270939ec2 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -2,6 +2,7 @@ #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] +#![allow(clippy::map_identity)] struct Deep(Option); @@ -40,7 +41,7 @@ fn main() { let _ = opt.or_else(|| None); let _ = opt.get_or_insert_with(|| 2); let _ = opt.ok_or_else(|| 2); - let _ = opt.ok_or_else(|| ext_arr[0]); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or_else(|| 2); @@ -60,7 +61,6 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = nested_opt.unwrap_or_else(|| Some(some_call())); - let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); @@ -69,10 +69,13 @@ fn main() { let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); + let _ = opt.ok_or_else(|| ext_arr[0]); - // These are handled by bind_instead_of_map + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); + + // should lint, bind_instead_of_map doesn't apply let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); @@ -92,17 +95,22 @@ fn main() { let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + // should not lint, bind_instead_of_map takes priority let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); - - let _: Result = res.or_else(|err| Ok(err)); let _: Result = res.or_else(|err| Err(err)); - // These are handled by bind_instead_of_map let _: Result = res.and_then(|_| Ok(2)); let _: Result = res.and_then(|_| Ok(astronomers_pi)); let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); + + // should lint, bind_instead_of_map doesn't apply + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.and_then(|_| Err(2)); let _: Result = res.and_then(|_| Err(astronomers_pi)); let _: Result = res.and_then(|_| Err(ext_str.some_field)); @@ -110,8 +118,4 @@ fn main() { let _: Result = res.or_else(|_| Ok(2)); let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 5c1b2eb1f14..1cf7ac46346 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,142 +7,190 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 - | -LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` - -error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:39:13 + --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:13 + --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:44:13 | -LL | let _ = opt.ok_or_else(|| ext_arr[0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` +LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:47:13 + --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:28 + --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:35 + --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:28 + --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:56:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $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)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $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)` +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:79:28 + | +LL | let _: Option = None.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.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))` + +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))` + error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:84:13 + --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:85:13 + --> $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)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:86:13 + --> $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)` -error: aborting due to 24 previous errors +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:114:35 + | +LL | let _: Result = res.and_then(|_| Err(2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:115:35 + | +LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:116:35 + | +LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:118:35 + | +LL | let _: Result = res.or_else(|_| Ok(2)); + | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:119:35 + | +LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:120:35 + | +LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` + +error: aborting due to 32 previous errors From d0ddbb9d0d0d6b9b6adb51b259819ec270421642 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Tue, 15 Sep 2020 21:10:50 -0600 Subject: [PATCH 0610/1110] Extend testing of rc_buffer lint --- tests/ui/rc_buffer.rs | 23 +++++++--- tests/ui/rc_buffer.stderr | 50 ++++++++++++++++------ tests/ui/rc_buffer_arc.rs | 24 ++++++++--- tests/ui/rc_buffer_arc.stderr | 50 ++++++++++++++++------ tests/ui/rc_buffer_redefined_string.rs | 12 ++++++ tests/ui/rc_buffer_redefined_string.stderr | 0 6 files changed, 122 insertions(+), 37 deletions(-) create mode 100644 tests/ui/rc_buffer_redefined_string.rs create mode 100644 tests/ui/rc_buffer_redefined_string.stderr diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs index c8c2bec67ee..1fa98643936 100644 --- a/tests/ui/rc_buffer.rs +++ b/tests/ui/rc_buffer.rs @@ -1,13 +1,26 @@ +#![warn(clippy::rc_buffer)] + +use std::cell::RefCell; use std::ffi::OsString; use std::path::PathBuf; use std::rc::Rc; -#[warn(clippy::rc_buffer)] struct S { - a: Rc, - b: Rc, - c: Rc>, - d: Rc, + // triggers lint + bad1: Rc, + bad2: Rc, + bad3: Rc>, + bad4: Rc, + // does not trigger lint + good1: Rc>, } +// triggers lint +fn func_bad1(_: Rc) {} +fn func_bad2(_: Rc) {} +fn func_bad3(_: Rc>) {} +fn func_bad4(_: Rc) {} +// does not trigger lint +fn func_good1(_: Rc>) {} + fn main() {} diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr index 641a13a2251..e4cc169af07 100644 --- a/tests/ui/rc_buffer.stderr +++ b/tests/ui/rc_buffer.stderr @@ -1,28 +1,52 @@ error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:7:8 + --> $DIR/rc_buffer.rs:10:11 | -LL | a: Rc, - | ^^^^^^^^^^ help: try: `Rc` +LL | bad1: Rc, + | ^^^^^^^^^^ help: try: `Rc` | = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:8:8 + --> $DIR/rc_buffer.rs:11:11 | -LL | b: Rc, - | ^^^^^^^^^^^ help: try: `Rc` +LL | bad2: Rc, + | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:9:8 + --> $DIR/rc_buffer.rs:12:11 | -LL | c: Rc>, - | ^^^^^^^^^^^ help: try: `Rc<[u8]>` +LL | bad3: Rc>, + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:10:8 + --> $DIR/rc_buffer.rs:13:11 | -LL | d: Rc, - | ^^^^^^^^^^^^ help: try: `Rc` +LL | bad4: Rc, + | ^^^^^^^^^^^^ help: try: `Rc` -error: aborting due to 4 previous errors +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:19:17 + | +LL | fn func_bad1(_: Rc) {} + | ^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:20:17 + | +LL | fn func_bad2(_: Rc) {} + | ^^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:21:17 + | +LL | fn func_bad3(_: Rc>) {} + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:22:17 + | +LL | fn func_bad4(_: Rc) {} + | ^^^^^^^^^^^^ help: try: `Rc` + +error: aborting due to 8 previous errors diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs index a878b0ab336..5d586584817 100644 --- a/tests/ui/rc_buffer_arc.rs +++ b/tests/ui/rc_buffer_arc.rs @@ -1,13 +1,25 @@ +#![warn(clippy::rc_buffer)] + use std::ffi::OsString; use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; -#[warn(clippy::rc_buffer)] struct S { - a: Arc, - b: Arc, - c: Arc>, - d: Arc, + // triggers lint + bad1: Arc, + bad2: Arc, + bad3: Arc>, + bad4: Arc, + // does not trigger lint + good1: Arc>, } +// triggers lint +fn func_bad1(_: Arc) {} +fn func_bad2(_: Arc) {} +fn func_bad3(_: Arc>) {} +fn func_bad4(_: Arc) {} +// does not trigger lint +fn func_good1(_: Arc>) {} + fn main() {} diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr index c4b01621046..8252270d2ac 100644 --- a/tests/ui/rc_buffer_arc.stderr +++ b/tests/ui/rc_buffer_arc.stderr @@ -1,28 +1,52 @@ error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:7:8 + --> $DIR/rc_buffer_arc.rs:9:11 | -LL | a: Arc, - | ^^^^^^^^^^^ help: try: `Arc` +LL | bad1: Arc, + | ^^^^^^^^^^^ help: try: `Arc` | = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:8:8 + --> $DIR/rc_buffer_arc.rs:10:11 | -LL | b: Arc, - | ^^^^^^^^^^^^ help: try: `Arc` +LL | bad2: Arc, + | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:9:8 + --> $DIR/rc_buffer_arc.rs:11:11 | -LL | c: Arc>, - | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` +LL | bad3: Arc>, + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:10:8 + --> $DIR/rc_buffer_arc.rs:12:11 | -LL | d: Arc, - | ^^^^^^^^^^^^^ help: try: `Arc` +LL | bad4: Arc, + | ^^^^^^^^^^^^^ help: try: `Arc` -error: aborting due to 4 previous errors +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:18:17 + | +LL | fn func_bad1(_: Arc) {} + | ^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:19:17 + | +LL | fn func_bad2(_: Arc) {} + | ^^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:20:17 + | +LL | fn func_bad3(_: Arc>) {} + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:21:17 + | +LL | fn func_bad4(_: Arc) {} + | ^^^^^^^^^^^^^ help: try: `Arc` + +error: aborting due to 8 previous errors diff --git a/tests/ui/rc_buffer_redefined_string.rs b/tests/ui/rc_buffer_redefined_string.rs new file mode 100644 index 00000000000..5d31a848cf7 --- /dev/null +++ b/tests/ui/rc_buffer_redefined_string.rs @@ -0,0 +1,12 @@ +#![warn(clippy::rc_buffer)] + +use std::rc::Rc; + +struct String; + +struct S { + // does not trigger lint + good1: Rc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer_redefined_string.stderr b/tests/ui/rc_buffer_redefined_string.stderr new file mode 100644 index 00000000000..e69de29bb2d From ad6f8c6354f4f1bba8101886646929a4985dec77 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 16 Sep 2020 12:09:57 +0700 Subject: [PATCH 0611/1110] bump pulldown-cmark v0.8 --- clippy_lints/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index cc7d3a04f00..341d9e601ee 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -21,7 +21,7 @@ cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" -pulldown-cmark = { version = "0.7.1", default-features = false } +pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } From 9c546b51d52673372f9e956c8e8c07f6d9d94f51 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 16 Sep 2020 16:38:58 +0200 Subject: [PATCH 0612/1110] Add S-* label modifier to triagebot --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 411229935c3..ed3c83af616 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,6 +1,6 @@ [relabel] allow-unauthenticated = [ - "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", + "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", "S-*", "good first issue", "needs test" ] From b37e3cdd46e715cacf96522e753829a7991e6fdf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 17 Sep 2020 00:06:43 +0900 Subject: [PATCH 0613/1110] Update documentation about moving from Discord to Zulip --- CONTRIBUTING.md | 6 +++--- doc/adding_lints.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 54777810abb..100c9edb367 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ something. We appreciate any sort of contributions, and don't want a wall of rul Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document explains how you can contribute and how to get started. If you have any questions about contributing or need help with -anything, feel free to ask questions on issues or visit the `#clippy` on [Discord]. +anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip]. All contributors are expected to follow the [Rust Code of Conduct]. @@ -23,7 +23,7 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) -[Discord]: https://discord.gg/rust-lang +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct ## Getting started @@ -242,7 +242,7 @@ to be run inside the `rust` directory): ``` 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or - ~~annoy~~ ask them in the [Discord] channel.) + ~~annoy~~ ask them in the [Zulip] stream.) ### Syncing back changes in Clippy to [`rust-lang/rust`] diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 3c782e9b17f..21e0f6f4fc7 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -488,7 +488,7 @@ For `LateLintPass` lints: While most of Clippy's lint utils are documented, most of rustc's internals lack documentation currently. This is unfortunate, but in most cases you can probably get away with copying things from existing similar lints. If you are stuck, -don't hesitate to ask on [Discord] or in the issue/PR. +don't hesitate to ask on [Zulip] or in the issue/PR. [utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs [if_chain]: https://docs.rs/if_chain/*/if_chain/ @@ -500,4 +500,4 @@ don't hesitate to ask on [Discord] or in the issue/PR. [nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ [ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html [ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html -[Discord]: https://discord.gg/rust-lang +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy From 0261e341fd435f0b42954510fb436cb0b289cdac Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 14 Sep 2020 14:58:39 -0400 Subject: [PATCH 0614/1110] {print,write}-with-newline: do not suggest empty format string --- clippy_lints/src/write.rs | 6 +++++- tests/ui/print_with_newline.rs | 1 + tests/ui/print_with_newline.stderr | 23 +++++++++++++++++------ tests/ui/write_with_newline.rs | 1 + tests/ui/write_with_newline.stderr | 23 +++++++++++++++++------ 5 files changed, 41 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index e653240d049..fac63bcb993 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -322,11 +322,15 @@ impl EarlyLintPass for Write { } /// Given a format string that ends in a newline and its span, calculates the span of the -/// newline. +/// newline, or the format string itself if the format string consists solely of a newline. fn newline_span(fmtstr: &StrLit) -> Span { let sp = fmtstr.span; let contents = &fmtstr.symbol.as_str(); + if *contents == r"\n" { + return sp; + } + let newline_sp_hi = sp.hi() - match fmtstr.style { StrStyle::Cooked => BytePos(1), diff --git a/tests/ui/print_with_newline.rs b/tests/ui/print_with_newline.rs index 3f710540e90..a43a1fc4f52 100644 --- a/tests/ui/print_with_newline.rs +++ b/tests/ui/print_with_newline.rs @@ -9,6 +9,7 @@ fn main() { print!("Hello {}\n", "world"); print!("Hello {} {}\n", "world", "#2"); print!("{}\n", 1265); + print!("\n"); // these are all fine print!(""); diff --git a/tests/ui/print_with_newline.stderr b/tests/ui/print_with_newline.stderr index 05fe88915d6..54b3ad75b31 100644 --- a/tests/ui/print_with_newline.stderr +++ b/tests/ui/print_with_newline.stderr @@ -44,7 +44,18 @@ LL | println!("{}", 1265); | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:30:5 + --> $DIR/print_with_newline.rs:12:5 + | +LL | print!("/n"); + | ^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!(); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:31:5 | LL | print!("//n"); // should fail | ^^^^^^^^^^^^^^ @@ -55,7 +66,7 @@ LL | println!("/"); // should fail | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:37:5 + --> $DIR/print_with_newline.rs:38:5 | LL | / print!( LL | | " @@ -70,7 +81,7 @@ LL | "" | error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:41:5 + --> $DIR/print_with_newline.rs:42:5 | LL | / print!( LL | | r" @@ -85,7 +96,7 @@ LL | r"" | error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:49:5 + --> $DIR/print_with_newline.rs:50:5 | LL | print!("/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^ @@ -96,7 +107,7 @@ LL | println!("/r"); //~ ERROR | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:50:5 + --> $DIR/print_with_newline.rs:51:5 | LL | print!("foo/rbar/n") // ~ ERROR | ^^^^^^^^^^^^^^^^^^^^ @@ -106,5 +117,5 @@ help: use `println!` instead LL | println!("foo/rbar") // ~ ERROR | ^^^^^^^ -- -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index 93afd73d111..1c1b1b58402 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -14,6 +14,7 @@ fn main() { write!(&mut v, "Hello {}\n", "world"); write!(&mut v, "Hello {} {}\n", "world", "#2"); write!(&mut v, "{}\n", 1265); + write!(&mut v, "\n"); // These should be fine write!(&mut v, ""); diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 2473329ca72..a14e86122ee 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -44,7 +44,18 @@ LL | writeln!(&mut v, "{}", 1265); | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:35:5 + --> $DIR/write_with_newline.rs:17:5 + | +LL | write!(&mut v, "/n"); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, ); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:36:5 | LL | write!(&mut v, "//n"); // should fail | ^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +66,7 @@ LL | writeln!(&mut v, "/"); // should fail | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:42:5 + --> $DIR/write_with_newline.rs:43:5 | LL | / write!( LL | | &mut v, @@ -72,7 +83,7 @@ LL | "" | error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:47:5 + --> $DIR/write_with_newline.rs:48:5 | LL | / write!( LL | | &mut v, @@ -89,7 +100,7 @@ LL | r"" | error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:56:5 + --> $DIR/write_with_newline.rs:57:5 | LL | write!(&mut v, "/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +111,7 @@ LL | writeln!(&mut v, "/r"); //~ ERROR | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:57:5 + --> $DIR/write_with_newline.rs:58:5 | LL | write!(&mut v, "foo/rbar/n"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,5 +121,5 @@ help: use `writeln!()` instead LL | writeln!(&mut v, "foo/rbar"); | ^^^^^^^ -- -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors From 79da7474b160e2230467f84ff0df3a23658eb4a6 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 16 Sep 2020 19:59:51 +0200 Subject: [PATCH 0615/1110] option_if_let_else - change misleading test file section --- tests/ui/unnecessary_lazy_eval.fixed | 7 ++++--- tests/ui/unnecessary_lazy_eval.rs | 7 ++++--- tests/ui/unnecessary_lazy_eval.stderr | 12 ++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 4980c111499..4ba2a0a5dbc 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -108,9 +108,6 @@ fn main() { let _: Result = res.or_else(|_| Err(ext_str.some_field)); // should lint, bind_instead_of_map doesn't apply - let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.and(Err(2)); let _: Result = res.and(Err(astronomers_pi)); let _: Result = res.and(Err(ext_str.some_field)); @@ -118,4 +115,8 @@ fn main() { let _: Result = res.or(Ok(2)); let _: Result = res.or(Ok(astronomers_pi)); let _: Result = res.or(Ok(ext_str.some_field)); + + // neither bind_instead_of_map nor unnecessary_lazy_eval applies here + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 0b270939ec2..466915217e4 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -108,9 +108,6 @@ fn main() { let _: Result = res.or_else(|_| Err(ext_str.some_field)); // should lint, bind_instead_of_map doesn't apply - let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.and_then(|_| Err(2)); let _: Result = res.and_then(|_| Err(astronomers_pi)); let _: Result = res.and_then(|_| Err(ext_str.some_field)); @@ -118,4 +115,8 @@ fn main() { let _: Result = res.or_else(|_| Ok(2)); let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + // neither bind_instead_of_map nor unnecessary_lazy_eval applies here + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 1cf7ac46346..44dcd0cafbb 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -157,37 +157,37 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:114:35 + --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:115:35 + --> $DIR/unnecessary_lazy_eval.rs:112:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:116:35 + --> $DIR/unnecessary_lazy_eval.rs:113:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:118:35 + --> $DIR/unnecessary_lazy_eval.rs:115:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:119:35 + --> $DIR/unnecessary_lazy_eval.rs:116:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:120:35 + --> $DIR/unnecessary_lazy_eval.rs:117:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` From 2ce2d6b40e85bf543b3a9e38cd08c7065b357807 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 11 Sep 2020 14:41:54 +1200 Subject: [PATCH 0616/1110] fix a FP in `indexing_slicing` treat refs to arrays the same as arrays in `indexing_slicing` and `out_of_bounds_indexing` --- clippy_lints/src/indexing_slicing.rs | 2 +- tests/ui/indexing_slicing_index.rs | 3 ++- tests/ui/indexing_slicing_index.stderr | 20 ++++++-------------- tests/ui/indexing_slicing_slice.stderr | 26 +++++++------------------- 4 files changed, 16 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index a28eda8be15..741195f3b10 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -88,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Index(ref array, ref index) = &expr.kind { - let ty = cx.typeck_results().expr_ty(array); + let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 000d5269930..ca8ca53c80c 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -15,7 +15,8 @@ fn main() { x[3]; // Ok, should not produce stderr. let y = &x; - y[0]; + y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 + y[4]; // Ok, rustc will handle references too. let v = vec![0; 5]; v[0]; diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 2b3f9be2dfb..2f6c9e2f4e5 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -8,15 +8,7 @@ LL | x[index]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:18:5 - | -LL | y[0]; - | ^^^^ - | - = help: Consider using `.get(n)` or `.get_mut(n)` instead - -error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:21:5 + --> $DIR/indexing_slicing_index.rs:22:5 | LL | v[0]; | ^^^^ @@ -24,7 +16,7 @@ LL | v[0]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:22:5 + --> $DIR/indexing_slicing_index.rs:23:5 | LL | v[10]; | ^^^^^ @@ -32,7 +24,7 @@ LL | v[10]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:23:5 + --> $DIR/indexing_slicing_index.rs:24:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -40,7 +32,7 @@ LL | v[1 << 3]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:29:5 + --> $DIR/indexing_slicing_index.rs:30:5 | LL | v[N]; | ^^^^ @@ -48,12 +40,12 @@ LL | v[N]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:30:5 + --> $DIR/indexing_slicing_index.rs:31:5 | LL | v[M]; | ^^^^ | = help: Consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index ec6c157ac1a..2231deee833 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -71,29 +71,17 @@ LL | &x[1..][..5]; | = help: Consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:24:6 - | -LL | &y[1..2]; - | ^^^^^^^ - | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead - -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:25:6 +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:25:12 | LL | &y[0..=4]; - | ^^^^^^^^ - | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^ -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:26:6 +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:26:11 | LL | &y[..=4]; - | ^^^^^^^ - | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + | ^ error: slicing may panic. --> $DIR/indexing_slicing_slice.rs:31:6 @@ -133,5 +121,5 @@ LL | &v[..100]; | = help: Consider using `.get(..n)`or `.get_mut(..n)` instead -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors From ce064722469672a504d8a1720ab4d2f681c21610 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 11 Sep 2020 15:06:49 +1200 Subject: [PATCH 0617/1110] replace `walk_ptrs_ty` with `peel_refs` --- clippy_lints/src/bytecount.rs | 7 +++---- clippy_lints/src/duration_subsec.rs | 4 ++-- clippy_lints/src/entry.rs | 4 ++-- clippy_lints/src/fallible_impl_from.rs | 6 ++---- clippy_lints/src/format.rs | 4 ++-- clippy_lints/src/inherent_to_string.rs | 4 ++-- clippy_lints/src/len_zero.rs | 4 ++-- clippy_lints/src/match_on_vec_items.rs | 5 ++--- clippy_lints/src/matches.rs | 4 ++-- clippy_lints/src/methods/mod.rs | 22 ++++++++++----------- clippy_lints/src/misc.rs | 6 +++--- clippy_lints/src/mut_key.rs | 4 ++-- clippy_lints/src/open_options.rs | 6 +++--- clippy_lints/src/path_buf_push_overwrite.rs | 4 ++-- clippy_lints/src/repeat_once.rs | 4 ++-- clippy_lints/src/strings.rs | 4 ++-- clippy_lints/src/swap.rs | 3 +-- clippy_lints/src/unwrap_in_result.rs | 6 +++--- clippy_lints/src/utils/internal_lints.rs | 6 +++--- clippy_lints/src/utils/mod.rs | 8 -------- 20 files changed, 51 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 189c07427ae..d7d02ebf985 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,6 +1,5 @@ use crate::utils::{ - contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, - span_lint_and_sugg, walk_ptrs_ty, + contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_ast::ast::UintTy; @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; if op.node == BinOpKind::Eq; if match_type(cx, - walk_ptrs_ty(cx.typeck_results().expr_ty(&filter_args[0])), + cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), &paths::SLICE_ITER); then { let needle = match get_path_name(l) { @@ -63,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { _ => { return; } } }; - if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() { + if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() { return; } let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 8ece44878fe..c0529a34cc4 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::Spanned; use crate::consts::{constant, Constant}; use crate::utils::paths; -use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind; if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind; - if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::DURATION); + if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION); if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right); then { let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index d616502a82a..35a5d00f4aa 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,6 +1,6 @@ use crate::utils::SpanlessEq; use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt}; -use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty}; +use crate::utils::{snippet_with_applicability, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -106,7 +106,7 @@ fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind; then { let map = ¶ms[0]; - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(map)); + let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); return if match_type(cx, obj_ty, &paths::BTREEMAP) { Some(("BTreeMap", map, key)) diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 000762334f6..a9e05fddbe7 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,5 @@ use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT}; -use crate::utils::{ - is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty, -}; +use crate::utils::{is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -96,7 +94,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { - let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 8bd85af8768..d6541010bca 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,7 +1,7 @@ use crate::utils::paths; use crate::utils::{ is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, - span_lint_and_then, walk_ptrs_ty, + span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -90,7 +90,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind; if pats.len() == 1; then { - let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0])); + let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { return None; } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index f330fa8fab8..0877b44d901 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -5,7 +5,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::{ get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, - trait_ref_of_method, walk_ptrs_ty, + trait_ref_of_method, }; declare_clippy_lint! { @@ -125,7 +125,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { // Get the real type of 'self' let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id); let self_type = cx.tcx.fn_sig(fn_def_id).input(0); - let self_type = walk_ptrs_ty(self_type.skip_binder()); + let self_type = self_type.skip_binder().peel_refs(); // Emit either a warning or an error if implements_trait(cx, self_type, display_trait_id, &[]) { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 42a98dc963d..c9c4891bb08 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -285,7 +285,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); + let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 57966452253..331b6c6c34a 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,3 @@ -use crate::utils::walk_ptrs_ty; use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -90,12 +89,12 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - let ty = walk_ptrs_ty(ty); + let ty = ty.peel_refs(); is_type_diagnostic_item(cx, ty, sym!(vec_type)) } fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - let ty = walk_ptrs_ty(ty); + let ty = ty.peel_refs(); is_type_lang_item(cx, ty, LangItem::RangeFull) } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7ba7397c29c..11380f83316 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -6,7 +6,7 @@ use crate::utils::{ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, walk_ptrs_ty, + span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -794,7 +794,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms } fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex)); + let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) { for arm in arms { if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 184aee95efa..dadd0f8ebb7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,8 +32,8 @@ use crate::utils::{ is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, + SpanlessEq, }; declare_clippy_lint! { @@ -1774,7 +1774,7 @@ fn lint_or_fun_call<'tcx>( ) { if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { if path.ident.as_str() == "len" { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); match ty.kind() { ty::Slice(_) | ty::Array(_, _) => return, @@ -1881,7 +1881,7 @@ fn lint_expect_fun_call( && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref)) && { let arg_type = cx.typeck_results().expr_ty(&call_args[0]); - let base_type = walk_ptrs_ty(arg_type); + let base_type = arg_type.peel_refs(); *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) } { @@ -2142,7 +2142,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp } fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg)); + let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs(); if let ty::Adt(_, subst) = obj_ty.kind() { let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { @@ -2173,7 +2173,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E let arg = &args[1]; if let Some(arglists) = method_chain_args(arg, &["chars"]) { let target = &arglists[0][0]; - let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target)); + let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { "" } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { @@ -2201,7 +2201,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E } fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) { lint_string_extend(cx, expr, args); } @@ -2384,7 +2384,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ } } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) || matches!( - &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(), + &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), ty::Array(_, _) ) { @@ -2587,7 +2587,7 @@ fn derefs_to_slice<'tcx>( /// lint use of `unwrap()` for `Option`s and `Result`s fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&unwrap_args[0])); + let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { Some((UNWRAP_USED, "an Option", "None")) @@ -2615,7 +2615,7 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E /// lint use of `expect()` for `Option`s and `Result`s fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&expect_args[0])); + let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { Some((EXPECT_USED, "an Option", "None")) @@ -3134,7 +3134,7 @@ fn lint_chars_cmp( if segment.ident.name == sym!(Some); then { let mut applicability = Applicability::MachineApplicable; - let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0])); + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); if *self_ty.kind() != ty::Str { return false; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 67a3685fd0d..909e79f661a 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -17,7 +17,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq, + span_lint_and_then, span_lint_hir_and_then, SpanlessEq, }; declare_clippy_lint! { @@ -561,7 +561,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(); + let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind(); if let ty::Array(arr_ty, _) = value { return matches!(arr_ty.kind(), ty::Float(_)); @@ -571,7 +571,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _)) + matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 0826ad0ab55..8a2dbdc50ea 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty}; +use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; @@ -98,7 +98,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir:: // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased // generics (because the compiler cannot ensure immutability for unknown types). fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { - let ty = walk_ptrs_ty(ty); + let ty = ty.peel_refs(); if let Adt(def, substs) = ty.kind() { if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET] .iter() diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index e99d0317ba2..73a99a3a2f8 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty}; +use crate::utils::{match_type, paths, span_lint}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -30,7 +30,7 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); impl<'tcx> LateLintPass<'tcx> for OpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0])); + let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { let mut options = Vec::new(); get_open_options(cx, &arguments[0], &mut options); @@ -58,7 +58,7 @@ enum OpenOption { fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0])); + let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); // Only proceed if this is a call on some object of type std::fs::OpenOptions if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 { diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index b8583402928..6eeb031d383 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; if path.ident.name == sym!(push); if args.len() == 2; - if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::PATH_BUF); + if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::PATH_BUF); if let Some(get_index_arg) = args.get(1); if let ExprKind::Lit(ref lit) = get_index_arg.kind; if let LitKind::Str(ref path_lit, _) = lit.node; diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index c0890018d46..ae601353009 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); + let ty = cx.typeck_results().expr_ty(&receiver).peel_refs(); if ty.is_str() { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 7a659bf779c..15b66684eab 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned; use if_chain::if_chain; use crate::utils::SpanlessEq; -use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without @@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(e)), sym!(string_type)) + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type)) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 47a73ca9a24..54b38d9f4ce 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,6 @@ use crate::utils::sugg::Sugg; use crate::utils::{ differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, - walk_ptrs_ty, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -194,7 +193,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { if eq_expr_value(cx, lhs1, lhs2) { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); + let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 1c7e62ecd3d..0f8797243ec 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // check for `expect` if let Some(arglists) = method_chain_args(expr, &["expect"]) { - let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) { @@ -91,7 +91,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { - let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) { diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index f201494a024..bfe426a25eb 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,6 +1,6 @@ use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, - snippet, span_lint, span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, + snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -427,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); then { @@ -460,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { let args = arg_lists[1]; if args.len() == 1; let self_arg = &args[0]; - let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(self_arg)); + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); then { span_lint_and_sugg( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 95eedf88178..ea52741b7cc 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -754,14 +754,6 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { } } -/// Returns the base type for references and raw pointers. -pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { - match ty.kind() { - ty::Ref(_, ty, _) => walk_ptrs_ty(ty), - _ => ty, - } -} - /// Returns the base type for references and raw pointers, and count reference /// depth. pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { From 27200855c34445bdf50185fbaed12b5c0d60eb9b Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Wed, 16 Sep 2020 17:19:35 -0600 Subject: [PATCH 0618/1110] Move rc_buffer lint into perf category --- clippy_lints/src/lib.rs | 3 ++- clippy_lints/src/types.rs | 2 +- src/lintlist/mod.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 239eeb10bb4..06f41be8a2a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1480,6 +1480,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::TYPE_COMPLEXITY), LintId::of(&types::UNIT_ARG), @@ -1780,6 +1781,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), + LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), ]); @@ -1805,7 +1807,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(&transmute::USELESS_TRANSMUTE), - LintId::of(&types::RC_BUFFER), LintId::of(&use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index da04d07885b..a29a199b8c3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -241,7 +241,7 @@ declare_clippy_lint! { /// fn foo(interned: Rc) { ... } /// ``` pub RC_BUFFER, - nursery, + perf, "shared ownership of a buffer type" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d0c6a1d63d9..e5563151b48 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1853,7 +1853,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "rc_buffer", - group: "nursery", + group: "perf", desc: "shared ownership of a buffer type", deprecation: None, module: "types", From d655c0a938c16d30ae6824713165c749eff7210f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 25 Aug 2020 15:13:40 +1200 Subject: [PATCH 0619/1110] Change the criteria of `interior_mutable_const` * stop linting associated types and generic type parameters * start linting ones in trait impls whose corresponding definitions in the traits are generic * remove the `is_copy` check as presumably the only purpose of it is to allow generics with `Copy` bounds as `Freeze` is internal and generics are no longer linted * remove the term 'copy' from the tests as being `Copy` no longer have meaning --- clippy_lints/src/non_copy_const.rs | 93 ++++++++++++------- tests/ui/borrow_interior_mutable_const.rs | 20 +++- tests/ui/borrow_interior_mutable_const.stderr | 46 +++++---- tests/ui/declare_interior_mutable_const.rs | 62 +++++++------ .../ui/declare_interior_mutable_const.stderr | 66 +++++-------- 5 files changed, 159 insertions(+), 128 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 73eabd4207e..28c68a2b68c 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -6,14 +6,17 @@ use std::ptr; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{Ty, TypeFlags}; +use rustc_middle::ty::fold::TypeFoldable as _; +use rustc_middle::ty::{AssocKind, Ty, TypeFlags}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then}; +use crate::utils::{in_constant, qpath_res, span_lint_and_then}; +use if_chain::if_chain; declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior @@ -83,11 +86,10 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[allow(dead_code)] #[derive(Copy, Clone)] enum Source { Item { item: Span }, - Assoc { item: Span, ty: Span }, + Assoc { item: Span }, Expr { expr: Span }, } @@ -110,10 +112,15 @@ impl Source { } fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { - if ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) || is_copy(cx, ty) { - // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which - // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze` - // as well. + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) { return; } @@ -127,11 +134,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { let const_kw_span = span.from_inner(InnerSpan::new(0, 5)); diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); }, - Source::Assoc { ty: ty_span, .. } => { - if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { - diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); - } - }, + Source::Assoc { .. } => (), Source::Expr { .. } => { diag.help("assign this const to a local or static variable, and use the variable here"); }, @@ -152,14 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound( - cx, - ty, - Source::Assoc { - ty: hir_ty.span, - item: trait_item.span, - }, - ); + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound. + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span }); } } @@ -167,17 +166,47 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); let item = cx.tcx.hir().expect_item(item_hir_id); - // Ensure the impl is an inherent impl. - if let ItemKind::Impl { of_trait: None, .. } = item.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound( - cx, - ty, - Source::Assoc { - ty: hir_ty.span, - item: impl_item.span, - }, - ); + + match &item.kind { + ItemKind::Impl { + of_trait: Some(of_trait_ref), + .. + } => { + if_chain! { + // Lint a trait impl item only when the definition is a generic type, + // assuming a assoc const is not meant to be a interior mutable type. + if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); + if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) + .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); + if cx.tcx + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound at the trait defs; + // and, in that case, the definition is *not* generic. + .normalize_erasing_regions( + cx.tcx.param_env(of_trait_def_id), + cx.tcx.type_of(of_assoc_item.def_id), + ) + .has_type_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_PARAM); + then { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound( + cx, + normalized, + Source::Assoc { + item: impl_item.span, + }, + ); + } + } + }, + ItemKind::Impl { of_trait: None, .. } => { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + // Normalize assoc types originated from generic params. + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span }); + }, + _ => (), } } } diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index 39f87510548..9fcc9ece49b 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -19,16 +19,30 @@ const NO_ANN: &dyn Display = &70; static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); const ONCE_INIT: Once = Once::new(); -trait Trait: Copy { - type NonCopyType; +trait Trait { + type AssocType; const ATOMIC: AtomicUsize; + const INPUT: T; + const ASSOC: Self::AssocType; + + fn function() { + let _ = &Self::INPUT; + let _ = &Self::ASSOC; + } } impl Trait for u64 { - type NonCopyType = u16; + type AssocType = AtomicUsize; const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INPUT: u32 = 10; + const ASSOC: Self::AssocType = AtomicUsize::new(11); + + fn function() { + let _ = &Self::INPUT; + let _ = &Self::ASSOC; //~ ERROR interior mutability + } } // This is just a pointer that can be safely dereferended, diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index 5800af7e960..ed726a6b46e 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,14 +1,22 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:66:5 + --> $DIR/borrow_interior_mutable_const.rs:44:18 | -LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability - | ^^^^^^ +LL | let _ = &Self::ASSOC; //~ ERROR interior mutability + | ^^^^^^^^^^^ | = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:16 + --> $DIR/borrow_interior_mutable_const.rs:80:5 + | +LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:81:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +24,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:70:22 + --> $DIR/borrow_interior_mutable_const.rs:84:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +32,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:71:25 + --> $DIR/borrow_interior_mutable_const.rs:85:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +40,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:72:27 + --> $DIR/borrow_interior_mutable_const.rs:86:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +48,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:73:26 + --> $DIR/borrow_interior_mutable_const.rs:87:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +56,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:84:14 + --> $DIR/borrow_interior_mutable_const.rs:98:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:85:14 + --> $DIR/borrow_interior_mutable_const.rs:99:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +72,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:86:19 + --> $DIR/borrow_interior_mutable_const.rs:100:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +80,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:87:14 + --> $DIR/borrow_interior_mutable_const.rs:101:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +88,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:88:13 + --> $DIR/borrow_interior_mutable_const.rs:102:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:94:13 + --> $DIR/borrow_interior_mutable_const.rs:108:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +104,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:99:5 + --> $DIR/borrow_interior_mutable_const.rs:113:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +112,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:100:16 + --> $DIR/borrow_interior_mutable_const.rs:114:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +120,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:113:5 + --> $DIR/borrow_interior_mutable_const.rs:127:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,12 +128,12 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:114:16 + --> $DIR/borrow_interior_mutable_const.rs:128:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index b4003ed8932..7471b360540 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -34,60 +34,64 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); #[allow(clippy::declare_interior_mutable_const)] const ONCE_INIT: Once = Once::new(); -trait Trait: Copy { - type NonCopyType; +struct Wrapper(T); + +trait Trait> { + type AssocType; + type AssocType2; + type AssocType3; const ATOMIC: AtomicUsize; //~ ERROR interior mutable const INTEGER: u64; const STRING: String; - const SELF: Self; // (no error) + const SELF: Self; const INPUT: T; - //~^ ERROR interior mutable - //~| HELP consider requiring `T` to be `Copy` - const ASSOC: Self::NonCopyType; - //~^ ERROR interior mutable - //~| HELP consider requiring `>::NonCopyType` to be `Copy` + const INPUT_ASSOC: T::AssocType4; + const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable + const ASSOC: Self::AssocType; + const ASSOC_2: Self::AssocType2; + const WRAPPED_ASSOC_2: Wrapper; + const WRAPPED_ASSOC_3: Wrapper; const AN_INPUT: T = Self::INPUT; - //~^ ERROR interior mutable - //~| ERROR consider requiring `T` to be `Copy` - declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable + declare_const!(ANOTHER_INPUT: T = Self::INPUT); + declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable } trait Trait2 { - type CopyType: Copy; + type AssocType4; + type AssocType5; const SELF_2: Self; - //~^ ERROR interior mutable - //~| HELP consider requiring `Self` to be `Copy` - const ASSOC_2: Self::CopyType; // (no error) + const ASSOC_4: Self::AssocType4; } -// we don't lint impl of traits, because an impl has no power to change the interface. -impl Trait for u64 { - type NonCopyType = u16; +impl> Trait for u64 { + type AssocType = u16; + type AssocType2 = AtomicUsize; + type AssocType3 = T; const ATOMIC: AtomicUsize = AtomicUsize::new(9); const INTEGER: u64 = 10; const STRING: String = String::new(); const SELF: Self = 11; - const INPUT: u32 = 12; - const ASSOC: Self::NonCopyType = 13; + const INPUT: T = T::SELF_2; + const INPUT_ASSOC: T::AssocType4 = T::ASSOC_4; + const INPUT_ASSOC_2: T::AssocType5 = AtomicUsize::new(16); + const ASSOC: Self::AssocType = 13; + const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable + const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable + const WRAPPED_ASSOC_3: Wrapper = Wrapper(T::SELF_2); } struct Local(T, U); -impl, U: Trait2> Local { - const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable +impl, U: Trait2> Local { + const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - const T_SELF: T = T::SELF_2; const U_SELF: U = U::SELF_2; - //~^ ERROR interior mutable - //~| HELP consider requiring `U` to be `Copy` - const T_ASSOC: T::NonCopyType = T::ASSOC; - //~^ ERROR interior mutable - //~| HELP consider requiring `>::NonCopyType` to be `Copy` - const U_ASSOC: U::CopyType = U::ASSOC_2; + const T_ASSOC: T::AssocType = T::ASSOC; + const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 6a9a57361f9..0fcb726db46 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -36,34 +36,16 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:40:5 + --> $DIR/declare_interior_mutable_const.rs:44:5 | LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:44:5 + --> $DIR/declare_interior_mutable_const.rs:50:5 | -LL | const INPUT: T; - | ^^^^^^^^^^^^^-^ - | | - | consider requiring `T` to be `Copy` - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:47:5 - | -LL | const ASSOC: Self::NonCopyType; - | ^^^^^^^^^^^^^-----------------^ - | | - | consider requiring `>::NonCopyType` to be `Copy` - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:51:5 - | -LL | const AN_INPUT: T = Self::INPUT; - | ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^ - | | - | consider requiring `T` to be `Copy` +LL | const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable --> $DIR/declare_interior_mutable_const.rs:16:9 @@ -71,40 +53,34 @@ error: a `const` item should never be interior mutable LL | const $name: $ty = $e; | ^^^^^^^^^^^^^^^^^^^^^^ ... -LL | declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable - | ----------------------------------------------- in this macro invocation +LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable + | ----------------------------------------------------------- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:60:5 + --> $DIR/declare_interior_mutable_const.rs:82:5 | -LL | const SELF_2: Self; - | ^^^^^^^^^^^^^^----^ - | | - | consider requiring `Self` to be `Copy` +LL | const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:81:5 + --> $DIR/declare_interior_mutable_const.rs:83:5 | -LL | const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable +LL | const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:90:5 + | +LL | const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:84:5 + --> $DIR/declare_interior_mutable_const.rs:94:5 | -LL | const U_SELF: U = U::SELF_2; - | ^^^^^^^^^^^^^^-^^^^^^^^^^^^^ - | | - | consider requiring `U` to be `Copy` +LL | const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:87:5 - | -LL | const T_ASSOC: T::NonCopyType = T::ASSOC; - | ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^ - | | - | consider requiring `>::NonCopyType` to be `Copy` - -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors From 2fc9064921ce0afd2c07c5b576f95c7adf731541 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 17 Sep 2020 19:37:42 +1200 Subject: [PATCH 0620/1110] rewrite the test and fix a minor fp * rewrite the test for `declare_interior_mutable_const from scratch` * fix a minor false positive where `Cell<"const T>` gets linted twice --- clippy_lints/src/non_copy_const.rs | 24 +-- tests/ui/declare_interior_mutable_const.rs | 161 +++++++++++++----- .../ui/declare_interior_mutable_const.stderr | 58 ++++--- 3 files changed, 167 insertions(+), 76 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 28c68a2b68c..bb44eeb6adc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -9,8 +9,7 @@ use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, Tr use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::fold::TypeFoldable as _; -use rustc_middle::ty::{AssocKind, Ty, TypeFlags}; +use rustc_middle::ty::{AssocKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -178,15 +177,18 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); - if cx.tcx - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - .normalize_erasing_regions( - cx.tcx.param_env(of_trait_def_id), - cx.tcx.type_of(of_assoc_item.def_id), - ) - .has_type_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_PARAM); + if cx + .tcx + .layout_of(cx.tcx.param_env(of_trait_def_id).and( + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound at the trait defs; + // and, in that case, the definition is *not* generic. + cx.tcx.normalize_erasing_regions( + cx.tcx.param_env(of_trait_def_id), + cx.tcx.type_of(of_assoc_item.def_id), + ), + )) + .is_err(); then { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index 7471b360540..646d3ec8b47 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -34,64 +34,135 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); #[allow(clippy::declare_interior_mutable_const)] const ONCE_INIT: Once = Once::new(); -struct Wrapper(T); - -trait Trait> { - type AssocType; - type AssocType2; - type AssocType3; - +// a constant whose type is a concrete type should be linted at the definition site. +trait ConcreteTypes { const ATOMIC: AtomicUsize; //~ ERROR interior mutable const INTEGER: u64; const STRING: String; - const SELF: Self; - const INPUT: T; - const INPUT_ASSOC: T::AssocType4; - const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable - const ASSOC: Self::AssocType; - const ASSOC_2: Self::AssocType2; - const WRAPPED_ASSOC_2: Wrapper; - const WRAPPED_ASSOC_3: Wrapper; - - const AN_INPUT: T = Self::INPUT; - declare_const!(ANOTHER_INPUT: T = Self::INPUT); declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable } -trait Trait2 { - type AssocType4; - type AssocType5; - - const SELF_2: Self; - const ASSOC_4: Self::AssocType4; -} - -impl> Trait for u64 { - type AssocType = u16; - type AssocType2 = AtomicUsize; - type AssocType3 = T; - +impl ConcreteTypes for u64 { const ATOMIC: AtomicUsize = AtomicUsize::new(9); const INTEGER: u64 = 10; const STRING: String = String::new(); - const SELF: Self = 11; - const INPUT: T = T::SELF_2; - const INPUT_ASSOC: T::AssocType4 = T::ASSOC_4; - const INPUT_ASSOC_2: T::AssocType5 = AtomicUsize::new(16); - const ASSOC: Self::AssocType = 13; - const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable - const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable - const WRAPPED_ASSOC_3: Wrapper = Wrapper(T::SELF_2); } -struct Local(T, U); +// a helper trait used below +trait ConstDefault { + const DEFAULT: Self; +} -impl, U: Trait2> Local { - const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable +// a constant whose type is a generic type should be linted at the implementation site. +trait GenericTypes { + const TO_REMAIN_GENERIC: T; + const TO_BE_CONCRETE: U; + + const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; + declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); +} + +impl GenericTypes for u64 { + const TO_REMAIN_GENERIC: T = T::DEFAULT; + const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable +} + +// a helper type used below +struct Wrapper(T); + +// a constant whose type is an associated type should be linted at the implementation site, too. +trait AssocTypes { + type ToBeFrozen; + type ToBeUnfrozen; + type ToBeGenericParam; + + const TO_BE_FROZEN: Self::ToBeFrozen; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen; + const WRAPPED_TO_BE_UNFROZEN: Wrapper; + // to ensure it can handle things when a generic type remains after normalization. + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; +} + +impl AssocTypes for Vec { + type ToBeFrozen = u16; + type ToBeUnfrozen = AtomicUsize; + type ToBeGenericParam = T; + + const TO_BE_FROZEN: Self::ToBeFrozen = 12; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); +} + +// a helper trait used below +trait AssocTypesHelper { + type NotToBeBounded; + type ToBeBounded; + + const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; +} + +// a constant whose type is an assoc type originated from a generic param bounded at the definition +// site should be linted at there. +trait AssocTypesFromGenericParam +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded; + const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable +} + +impl AssocTypesFromGenericParam for u64 +where + T: AssocTypesHelper, +{ + // an associated type could remain unknown in a trait impl. + const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); +} + +trait SelfType { + const SELF: Self; +} + +impl SelfType for u64 { + const SELF: Self = 16; +} + +impl SelfType for AtomicUsize { + // this (interior mutable `Self` const) exists in `parking_lot`. + // `const_trait_impl` will replace it in the future, hopefully. + const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable +} + +// Even though a constant contains a generic type, if it also have a interior mutable type, +// it should be linted at the definition site. +trait BothOfCellAndGeneric { + // this is a false negative in the current implementation. + const DIRECT: Cell; + const INDIRECT: Cell<*const T>; //~ ERROR interior mutable +} + +impl BothOfCellAndGeneric for u64 { + const DIRECT: Cell = Cell::new(T::DEFAULT); + const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); +} + +struct Local(T); + +// a constant in an inherent impl are essentially the same as a normal const item +// except there can be a generic or associated type. +impl Local +where + T: ConstDefault + AssocTypesHelper, +{ + const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - const U_SELF: U = U::SELF_2; - const T_ASSOC: T::AssocType = T::ASSOC; - const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable + + const GENERIC_TYPE: T = T::DEFAULT; + + const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 0fcb726db46..0a0b818b8b7 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -36,17 +36,11 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:44:5 + --> $DIR/declare_interior_mutable_const.rs:39:5 | LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:50:5 - | -LL | const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: a `const` item should never be interior mutable --> $DIR/declare_interior_mutable_const.rs:16:9 | @@ -59,28 +53,52 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR i = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:82:5 + --> $DIR/declare_interior_mutable_const.rs:67:5 | -LL | const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:83:5 + --> $DIR/declare_interior_mutable_const.rs:92:5 | -LL | const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:90:5 + --> $DIR/declare_interior_mutable_const.rs:93:5 | -LL | const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:94:5 + --> $DIR/declare_interior_mutable_const.rs:112:5 | -LL | const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:135:5 + | +LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:143:5 + | +LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:159:5 + | +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:165:5 + | +LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors From d5af360bb2de24235d2873e926d0b6f21135ae38 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 17 Sep 2020 21:14:14 +1200 Subject: [PATCH 0621/1110] add `WRAPPED_SELF: Option` in the test --- tests/ui/declare_interior_mutable_const.rs | 8 +++++++- tests/ui/declare_interior_mutable_const.stderr | 16 +++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index 646d3ec8b47..3afcdca2f04 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -121,18 +121,24 @@ where const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); } -trait SelfType { +// a constant whose type is `Self` should be linted at the implementation site as well. +// (`Option` requires `Sized` bound.) +trait SelfType: Sized { const SELF: Self; + // this was the one in the original issue (#5050). + const WRAPPED_SELF: Option; } impl SelfType for u64 { const SELF: Self = 16; + const WRAPPED_SELF: Option = Some(20); } impl SelfType for AtomicUsize { // this (interior mutable `Self` const) exists in `parking_lot`. // `const_trait_impl` will replace it in the future, hopefully. const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable + const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable } // Even though a constant contains a generic type, if it also have a interior mutable type, diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 0a0b818b8b7..5cb10be88d8 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -77,28 +77,34 @@ LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:135:5 + --> $DIR/declare_interior_mutable_const.rs:140:5 | LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:143:5 + --> $DIR/declare_interior_mutable_const.rs:141:5 + | +LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:149:5 | LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:159:5 + --> $DIR/declare_interior_mutable_const.rs:165:5 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:165:5 + --> $DIR/declare_interior_mutable_const.rs:171:5 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors From 4117ae1175430087441c9b34145f148d49c2f08c Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 21 Sep 2020 15:32:26 +0200 Subject: [PATCH 0622/1110] Split redundant_pattern_matching tests This is to avoid the 200 lines stderr file limit --- tests/ui/redundant_pattern_matching.fixed | 71 +------ tests/ui/redundant_pattern_matching.rs | 86 +-------- tests/ui/redundant_pattern_matching.stderr | 174 +++--------------- .../redundant_pattern_matching_option.fixed | 85 +++++++++ tests/ui/redundant_pattern_matching_option.rs | 100 ++++++++++ .../redundant_pattern_matching_option.stderr | 134 ++++++++++++++ 6 files changed, 351 insertions(+), 299 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_option.fixed create mode 100644 tests/ui/redundant_pattern_matching_option.rs create mode 100644 tests/ui/redundant_pattern_matching_option.stderr diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 8084fdefdc2..17d908336d5 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -18,39 +18,14 @@ fn main() { if Err::(42).is_err() {} - if None::<()>.is_none() {} - - if Some(42).is_some() {} - - if Some(42).is_some() { - foo(); - } else { - bar(); - } - - while Some(42).is_some() {} - - while Some(42).is_none() {} - - while None::<()>.is_none() {} - while Ok::(10).is_ok() {} while Ok::(10).is_err() {} - let mut v = vec![1, 2, 3]; - while v.pop().is_some() { - foo(); - } - if Ok::(42).is_ok() {} if Err::(42).is_err() {} - if None::.is_none() {} - - if Some(42).is_some() {} - if let Ok(x) = Ok::(42) { println!("{}", x); } @@ -63,48 +38,24 @@ fn main() { Err::(42).is_ok(); - Some(42).is_some(); - - None::<()>.is_none(); - - let _ = None::<()>.is_none(); - let _ = if Ok::(4).is_ok() { true } else { false }; - let opt = Some(false); - let x = if opt.is_some() { true } else { false }; - takes_bool(x); - issue5504(); issue6067(); - let _ = if gen_opt().is_some() { + let _ = if gen_res().is_ok() { 1 - } else if gen_opt().is_none() { - 2 - } else if gen_res().is_ok() { - 3 } else if gen_res().is_err() { - 4 + 2 } else { - 5 + 3 }; } -fn gen_opt() -> Option<()> { - None -} - fn gen_res() -> Result<(), ()> { Ok(()) } -fn takes_bool(_: bool) {} - -fn foo() {} - -fn bar() {} - macro_rules! m { () => { Some(42u32) @@ -129,30 +80,18 @@ fn issue5504() { } // Methods that are unstable const should not be suggested within a const context, see issue #5697. -// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none` -// of `Option` were stabilized as const, so the following should be linted. +// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, +// so the following should be linted. const fn issue6067() { if Ok::(42).is_ok() {} if Err::(42).is_err() {} - if Some(42).is_some() {} - - if None::<()>.is_none() {} - while Ok::(10).is_ok() {} while Ok::(10).is_err() {} - while Some(42).is_some() {} - - while None::<()>.is_none() {} - Ok::(42).is_ok(); Err::(42).is_err(); - - Some(42).is_some(); - - None::<()>.is_none(); } diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index 48a32cb1c7b..d57fbb14ae4 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -18,39 +18,14 @@ fn main() { if let Err(_) = Err::(42) {} - if let None = None::<()> {} - - if let Some(_) = Some(42) {} - - if let Some(_) = Some(42) { - foo(); - } else { - bar(); - } - - while let Some(_) = Some(42) {} - - while let None = Some(42) {} - - while let None = None::<()> {} - while let Ok(_) = Ok::(10) {} while let Err(_) = Ok::(10) {} - let mut v = vec![1, 2, 3]; - while let Some(_) = v.pop() { - foo(); - } - if Ok::(42).is_ok() {} if Err::(42).is_err() {} - if None::.is_none() {} - - if Some(42).is_some() {} - if let Ok(x) = Ok::(42) { println!("{}", x); } @@ -75,57 +50,24 @@ fn main() { Err(_) => false, }; - match Some(42) { - Some(_) => true, - None => false, - }; - - match None::<()> { - Some(_) => false, - None => true, - }; - - let _ = match None::<()> { - Some(_) => false, - None => true, - }; - let _ = if let Ok(_) = Ok::(4) { true } else { false }; - let opt = Some(false); - let x = if let Some(_) = opt { true } else { false }; - takes_bool(x); - issue5504(); issue6067(); - let _ = if let Some(_) = gen_opt() { + let _ = if let Ok(_) = gen_res() { 1 - } else if let None = gen_opt() { - 2 - } else if let Ok(_) = gen_res() { - 3 } else if let Err(_) = gen_res() { - 4 + 2 } else { - 5 + 3 }; } -fn gen_opt() -> Option<()> { - None -} - fn gen_res() -> Result<(), ()> { Ok(()) } -fn takes_bool(_: bool) {} - -fn foo() {} - -fn bar() {} - macro_rules! m { () => { Some(42u32) @@ -150,25 +92,17 @@ fn issue5504() { } // Methods that are unstable const should not be suggested within a const context, see issue #5697. -// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none` -// of `Option` were stabilized as const, so the following should be linted. +// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, +// so the following should be linted. const fn issue6067() { if let Ok(_) = Ok::(42) {} if let Err(_) = Err::(42) {} - if let Some(_) = Some(42) {} - - if let None = None::<()> {} - while let Ok(_) = Ok::(10) {} while let Err(_) = Ok::(10) {} - while let Some(_) = Some(42) {} - - while let None = None::<()> {} - match Ok::(42) { Ok(_) => true, Err(_) => false, @@ -178,14 +112,4 @@ const fn issue6067() { Ok(_) => false, Err(_) => true, }; - - match Some(42) { - Some(_) => true, - None => false, - }; - - match None::<()> { - Some(_) => false, - None => true, - }; } diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 17185217e89..955900f3e6c 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -18,62 +18,20 @@ error: redundant pattern matching, consider using `is_err()` LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:21:12 - | -LL | if let None = None::<()> {} - | -------^^^^------------- help: try this: `if None::<()>.is_none()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:23:12 - | -LL | if let Some(_) = Some(42) {} - | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:25:12 - | -LL | if let Some(_) = Some(42) { - | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:31:15 - | -LL | while let Some(_) = Some(42) {} - | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:33:15 - | -LL | while let None = Some(42) {} - | ----------^^^^----------- help: try this: `while Some(42).is_none()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:35:15 - | -LL | while let None = None::<()> {} - | ----------^^^^------------- help: try this: `while None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:37:15 + --> $DIR/redundant_pattern_matching.rs:21:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:39:15 + --> $DIR/redundant_pattern_matching.rs:23:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:42:15 - | -LL | while let Some(_) = v.pop() { - | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:58:5 + --> $DIR/redundant_pattern_matching.rs:33:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -82,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:63:5 + --> $DIR/redundant_pattern_matching.rs:38:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -91,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:68:5 + --> $DIR/redundant_pattern_matching.rs:43:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -100,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:73:5 + --> $DIR/redundant_pattern_matching.rs:48:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -108,144 +66,74 @@ LL | | Err(_) => false, LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:78:5 - | -LL | / match Some(42) { -LL | | Some(_) => true, -LL | | None => false, -LL | | }; - | |_____^ help: try this: `Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:83:5 - | -LL | / match None::<()> { -LL | | Some(_) => false, -LL | | None => true, -LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:88:13 - | -LL | let _ = match None::<()> { - | _____________^ -LL | | Some(_) => false, -LL | | None => true, -LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:53:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:96:20 - | -LL | let x = if let Some(_) = opt { true } else { false }; - | -------^^^^^^^------ help: try this: `if opt.is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:102:20 - | -LL | let _ = if let Some(_) = gen_opt() { - | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:104:19 - | -LL | } else if let None = gen_opt() { - | -------^^^^------------ help: try this: `if gen_opt().is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:106:19 + --> $DIR/redundant_pattern_matching.rs:58:20 | -LL | } else if let Ok(_) = gen_res() { - | -------^^^^^------------ help: try this: `if gen_res().is_ok()` +LL | let _ = if let Ok(_) = gen_res() { + | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:108:19 + --> $DIR/redundant_pattern_matching.rs:60:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:141:19 + --> $DIR/redundant_pattern_matching.rs:83:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:142:16 + --> $DIR/redundant_pattern_matching.rs:84:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:148:12 + --> $DIR/redundant_pattern_matching.rs:90:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:149:15 + --> $DIR/redundant_pattern_matching.rs:91:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:156:12 + --> $DIR/redundant_pattern_matching.rs:98:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:158:12 + --> $DIR/redundant_pattern_matching.rs:100:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:160:12 - | -LL | if let Some(_) = Some(42) {} - | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:162:12 - | -LL | if let None = None::<()> {} - | -------^^^^------------- help: try this: `if None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:164:15 + --> $DIR/redundant_pattern_matching.rs:102:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:166:15 + --> $DIR/redundant_pattern_matching.rs:104:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:168:15 - | -LL | while let Some(_) = Some(42) {} - | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:170:15 - | -LL | while let None = None::<()> {} - | ----------^^^^------------- help: try this: `while None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:172:5 + --> $DIR/redundant_pattern_matching.rs:106:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -254,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:177:5 + --> $DIR/redundant_pattern_matching.rs:111:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -262,23 +150,5 @@ LL | | Err(_) => true, LL | | }; | |_____^ help: try this: `Err::(42).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:182:5 - | -LL | / match Some(42) { -LL | | Some(_) => true, -LL | | None => false, -LL | | }; - | |_____^ help: try this: `Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:187:5 - | -LL | / match None::<()> { -LL | | Some(_) => false, -LL | | None => true, -LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` - -error: aborting due to 41 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed new file mode 100644 index 00000000000..499b975b2bb --- /dev/null +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -0,0 +1,85 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] + +fn main() { + if None::<()>.is_none() {} + + if Some(42).is_some() {} + + if Some(42).is_some() { + foo(); + } else { + bar(); + } + + while Some(42).is_some() {} + + while Some(42).is_none() {} + + while None::<()>.is_none() {} + + let mut v = vec![1, 2, 3]; + while v.pop().is_some() { + foo(); + } + + if None::.is_none() {} + + if Some(42).is_some() {} + + Some(42).is_some(); + + None::<()>.is_none(); + + let _ = None::<()>.is_none(); + + let opt = Some(false); + let x = if opt.is_some() { true } else { false }; + takes_bool(x); + + issue6067(); + + let _ = if gen_opt().is_some() { + 1 + } else if gen_opt().is_none() { + 2 + } else { + 3 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +// Methods that are unstable const should not be suggested within a const context, see issue #5697. +// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const, +// so the following should be linted. +const fn issue6067() { + if Some(42).is_some() {} + + if None::<()>.is_none() {} + + while Some(42).is_some() {} + + while None::<()>.is_none() {} + + Some(42).is_some(); + + None::<()>.is_none(); +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs new file mode 100644 index 00000000000..2a98435e790 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -0,0 +1,100 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] + +fn main() { + if let None = None::<()> {} + + if let Some(_) = Some(42) {} + + if let Some(_) = Some(42) { + foo(); + } else { + bar(); + } + + while let Some(_) = Some(42) {} + + while let None = Some(42) {} + + while let None = None::<()> {} + + let mut v = vec![1, 2, 3]; + while let Some(_) = v.pop() { + foo(); + } + + if None::.is_none() {} + + if Some(42).is_some() {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; + + let _ = match None::<()> { + Some(_) => false, + None => true, + }; + + let opt = Some(false); + let x = if let Some(_) = opt { true } else { false }; + takes_bool(x); + + issue6067(); + + let _ = if let Some(_) = gen_opt() { + 1 + } else if let None = gen_opt() { + 2 + } else { + 3 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +// Methods that are unstable const should not be suggested within a const context, see issue #5697. +// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const, +// so the following should be linted. +const fn issue6067() { + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr new file mode 100644 index 00000000000..eebb3448491 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -0,0 +1,134 @@ +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:14:12 + | +LL | if let None = None::<()> {} + | -------^^^^------------- help: try this: `if None::<()>.is_none()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:16:12 + | +LL | if let Some(_) = Some(42) {} + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:18:12 + | +LL | if let Some(_) = Some(42) { + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:24:15 + | +LL | while let Some(_) = Some(42) {} + | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:26:15 + | +LL | while let None = Some(42) {} + | ----------^^^^----------- help: try this: `while Some(42).is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:28:15 + | +LL | while let None = None::<()> {} + | ----------^^^^------------- help: try this: `while None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:31:15 + | +LL | while let Some(_) = v.pop() { + | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:39:5 + | +LL | / match Some(42) { +LL | | Some(_) => true, +LL | | None => false, +LL | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:44:5 + | +LL | / match None::<()> { +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:49:13 + | +LL | let _ = match None::<()> { + | _____________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:55:20 + | +LL | let x = if let Some(_) = opt { true } else { false }; + | -------^^^^^^^------ help: try this: `if opt.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:60:20 + | +LL | let _ = if let Some(_) = gen_opt() { + | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:62:19 + | +LL | } else if let None = gen_opt() { + | -------^^^^------------ help: try this: `if gen_opt().is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:83:12 + | +LL | if let Some(_) = Some(42) {} + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:85:12 + | +LL | if let None = None::<()> {} + | -------^^^^------------- help: try this: `if None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:87:15 + | +LL | while let Some(_) = Some(42) {} + | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:89:15 + | +LL | while let None = None::<()> {} + | ----------^^^^------------- help: try this: `while None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:91:5 + | +LL | / match Some(42) { +LL | | Some(_) => true, +LL | | None => false, +LL | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:96:5 + | +LL | / match None::<()> { +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: aborting due to 19 previous errors + From d4f158fa5cd5f4ae1cc690b37b0a8850cb013b69 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sun, 20 Sep 2020 12:38:23 +0300 Subject: [PATCH 0623/1110] Forbid redundant_pattern_matching triggering in macros - remove ice-2636 test --- clippy_lints/src/matches.rs | 2 +- tests/ui/crashes/ice-2636.rs | 22 -------------------- tests/ui/crashes/ice-2636.stderr | 17 --------------- tests/ui/redundant_pattern_matching.fixed | 12 +++++++++++ tests/ui/redundant_pattern_matching.rs | 12 +++++++++++ tests/ui/redundant_pattern_matching.stderr | 24 +++++++++++----------- 6 files changed, 37 insertions(+), 52 deletions(-) delete mode 100644 tests/ui/crashes/ice-2636.rs delete mode 100644 tests/ui/crashes/ice-2636.stderr diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 6f47687c410..b1a4e06d4c3 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -502,7 +502,7 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { return; } diff --git a/tests/ui/crashes/ice-2636.rs b/tests/ui/crashes/ice-2636.rs deleted file mode 100644 index e0b58157590..00000000000 --- a/tests/ui/crashes/ice-2636.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![allow(dead_code)] - -enum Foo { - A, - B, - C, -} - -macro_rules! test_hash { - ($foo:expr, $($t:ident => $ord:expr),+ ) => { - use self::Foo::*; - match $foo { - $ ( & $t => $ord, - )* - }; - }; -} - -fn main() { - let a = Foo::A; - test_hash!(&a, A => 0, B => 1, C => 2); -} diff --git a/tests/ui/crashes/ice-2636.stderr b/tests/ui/crashes/ice-2636.stderr deleted file mode 100644 index 53799b4fbf1..00000000000 --- a/tests/ui/crashes/ice-2636.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/ice-2636.rs:12:9 - | -LL | / match $foo { -LL | | $ ( & $t => $ord, -LL | | )* -LL | | }; - | |_________^ -... -LL | test_hash!(&a, A => 0, B => 1, C => 2); - | --------------------------------------- in this macro invocation - | - = note: `-D clippy::match-ref-pats` implied by `-D warnings` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 17d908336d5..fe8f62503b7 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -42,6 +42,7 @@ fn main() { issue5504(); issue6067(); + issue6065(); let _ = if gen_res().is_ok() { 1 @@ -79,6 +80,17 @@ fn issue5504() { while m!().is_some() {} } +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + // Methods that are unstable const should not be suggested within a const context, see issue #5697. // However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, // so the following should be linted. diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index d57fbb14ae4..09426a6e590 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -54,6 +54,7 @@ fn main() { issue5504(); issue6067(); + issue6065(); let _ = if let Ok(_) = gen_res() { 1 @@ -91,6 +92,17 @@ fn issue5504() { while let Some(_) = m!() {} } +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + // Methods that are unstable const should not be suggested within a const context, see issue #5697. // However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, // so the following should be linted. diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 955900f3e6c..3473ceea00e 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:58:20 + --> $DIR/redundant_pattern_matching.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:60:19 + --> $DIR/redundant_pattern_matching.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:83:19 + --> $DIR/redundant_pattern_matching.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:84:16 + --> $DIR/redundant_pattern_matching.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:90:12 + --> $DIR/redundant_pattern_matching.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:91:15 + --> $DIR/redundant_pattern_matching.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:98:12 + --> $DIR/redundant_pattern_matching.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:100:12 + --> $DIR/redundant_pattern_matching.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:102:15 + --> $DIR/redundant_pattern_matching.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:104:15 + --> $DIR/redundant_pattern_matching.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:106:5 + --> $DIR/redundant_pattern_matching.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:111:5 + --> $DIR/redundant_pattern_matching.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, From 5e393c7747d081c45414060f81016e9ea3cb961f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:05:32 +1200 Subject: [PATCH 0624/1110] Fix a FP in `explicit_counter_loop` Fix a false positive in `explicit_counter_loop` where the loop counter is used after incremented, adjust the test so that counters are incremented at the end of the loop and add the test for this false positive. --- clippy_lints/src/loops.rs | 6 +++++- tests/ui/explicit_counter_loop.rs | 29 +++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3410341a1e3..7f998c63f49 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2134,7 +2134,7 @@ enum VarState { DontWarn, } -/// Scan a for loop for variables that are incremented exactly once. +/// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: FxHashMap, // incremented variables @@ -2154,6 +2154,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(VarState::Initial); + if *state == VarState::IncrOnce { + *state = VarState::DontWarn; + return; + } match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa6ef162fe4..81d8221bd13 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -38,54 +38,54 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { continue; } count += 1; - println!("{}", count); } // should not trigger the lint because the count is conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { count += 1; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; if ch == 'a' { continue; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { let _ = 123; } - println!("{}", count); } // should not trigger the lint because the count is incremented multiple times let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { count += 1; } - println!("{}", count); } } } @@ -96,30 +96,30 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { + println!("{}", skips); while erasures.contains(&(i + skips)) { skips += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); let mut j = 0; while j < 5 { skips += 1; j += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); for j in 0..5 { skips += 1; } - println!("{}", skips); } } } @@ -145,3 +145,16 @@ mod issue_4732 { let _closure = || println!("index: {}", index); } } + +mod issue_4677 { + pub fn test() { + let slice = &[1, 2, 3]; + + // should not trigger the lint because the count is used after incremented + let mut count = 0; + for _i in slice { + count += 1; + println!("{}", count); + } + } +} From 3e294b22a43be349262405715cf4885296c284ba Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 00:34:56 +0200 Subject: [PATCH 0625/1110] Revert "or_fn_call: ignore nullary associated const fns" This reverts commit 7a66e6502dc3c7085b3f4078c01d4957d96175ed. --- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/or_fun_call.fixed | 17 ++++++----------- tests/ui/or_fun_call.rs | 17 ++++++----------- tests/ui/or_fun_call.stderr | 20 ++++++++++++++++---- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index ea52741b7cc..3a005e2513e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -893,7 +893,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5fb568672d3..67faa8bd4a0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -116,17 +122,6 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 737b0f7e55b..9867e2eedcf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -116,17 +122,6 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index b8a436993f3..bc5978b538f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,23 +60,35 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:62:21 + --> $DIR/or_fun_call.rs:68:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:87:35 + --> $DIR/or_fun_call.rs:93:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:91:10 + --> $DIR/or_fun_call.rs:97:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors From ce83d8d4d1b28e73888a616d3ffbf19c6a620588 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 00:39:00 +0200 Subject: [PATCH 0626/1110] Revert "Avoid or_fun_call for const_fn with no args" This reverts commit 5d66bd7bb3fd701d70ec11217e3f89fabe5cb0a7. --- clippy_lints/src/utils/mod.rs | 9 --------- tests/ui/or_fun_call.fixed | 8 -------- tests/ui/or_fun_call.rs | 8 -------- 3 files changed, 25 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a005e2513e..92cb31fcf85 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -46,7 +46,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; -use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -883,19 +882,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() - } - if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { - const_eval::is_const_fn(cx.tcx, def_id) - }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..2045ffdb5f0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -116,12 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..522f31b72d0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -116,12 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); -} - fn main() {} From 6c056d346562eebf8535e2a4415c353a911b6280 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Tue, 22 Sep 2020 20:34:38 -0600 Subject: [PATCH 0627/1110] Satisfy rc_buffer lint in Constant::Binary byte string by copying data We can avoid the data copy again by fixing rustc_ast::ast::LitKind later. --- clippy_lints/src/consts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 3ee022e4e68..0000d39263e 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -21,7 +21,7 @@ pub enum Constant { /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). - Binary(Lrc>), + Binary(Lrc<[u8]>), /// A single `char` (e.g., `'a'`). Char(char), /// An integer's bit representation. @@ -155,7 +155,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s) => Constant::Binary(Lrc::from(s.as_slice())), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { From 9365660a2fc57210996df733efe468019c671b2f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 23:33:50 +0200 Subject: [PATCH 0628/1110] unnecessary sort by: avoid dereferencing closure param --- clippy_lints/src/unnecessary_sort_by.rs | 58 +++++++++---------------- tests/ui/unnecessary_sort_by.fixed | 30 ++++++------- tests/ui/unnecessary_sort_by.rs | 10 ++--- tests/ui/unnecessary_sort_by.stderr | 52 +++++++++++++++++----- 4 files changed, 81 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 9b6a9075a29..1307237dbc7 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -170,22 +170,12 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, - // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested - // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more - // than one level of references would add some extra complexity as we would have to compensate - // in the closure body. - if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - let vec_ty = cx.typeck_results().expr_ty(vec); - if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec - if !matches!(&ty.kind(), ty::Ref(..)); - if utils::is_copy(cx, ty); + if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type)); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, @@ -210,40 +200,32 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - if_chain! { - if let ExprKind::Path(QPath::Resolved(_, Path { - segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind; - if left_name == left_ident; - then { - return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } else { - if !key_returns_borrow(cx, left_expr) { - return Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) - } + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind { + if left_name == left_ident { + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); } } + + if !expr_borrows(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })); + } } } None } -fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(def_id) = utils::fn_def_id(cx, expr) { - let output = cx.tcx.fn_sig(def_id).output(); - let ty = output.skip_binder(); - return matches!(ty.kind(), ty::Ref(..)) - || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - false +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } impl LateLintPass<'_> for UnnecessarySortBy { @@ -256,7 +238,7 @@ impl LateLintPass<'_> for UnnecessarySortBy { "use Vec::sort_by_key here instead", "try", format!( - "{}.sort{}_by_key(|&{}| {})", + "{}.sort{}_by_key(|{}| {})", trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index ad0d0387db0..b45b27d8f23 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -13,12 +13,12 @@ fn unnecessary_sort_by() { // Forward examples vec.sort(); vec.sort_unstable(); - vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_unstable_by_key(|&a| id(-a)); + vec.sort_by_key(|a| (a + 5).abs()); + vec.sort_unstable_by_key(|a| id(-a)); // Reverse examples - vec.sort_by_key(|&b| Reverse(b)); - vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_unstable_by_key(|&b| Reverse(id(-b))); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow + vec.sort_by_key(|b| Reverse((b + 5).abs())); + vec.sort_unstable_by_key(|b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; - vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); - vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by_key(|a| (***a).abs()); + vec.sort_unstable_by_key(|a| (***a).abs()); + // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { @@ -85,11 +85,11 @@ mod issue_6001 { let mut args: Vec = vec![]; // Forward - args.sort_by(|a, b| a.name().cmp(&b.name())); - args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + args.sort_by_key(|a| a.name()); + args.sort_unstable_by_key(|a| a.name()); // Reverse - args.sort_by(|a, b| b.name().cmp(&a.name())); - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + args.sort_by_key(|b| Reverse(b.name())); + args.sort_unstable_by_key(|b| Reverse(b.name())); } } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 9746f6e6849..be2abe7f701 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -16,7 +16,7 @@ fn unnecessary_sort_by() { vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples - vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 70c6cf0a3b6..50607933e18 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -16,31 +16,61 @@ error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` - -error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:19:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:20:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))` -error: aborting due to 7 previous errors +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:31:5 + | +LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:32:5 + | +LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:88:9 + | +LL | args.sort_by(|a, b| a.name().cmp(&b.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:89:9 + | +LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:91:9 + | +LL | args.sort_by(|a, b| b.name().cmp(&a.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| Reverse(b.name()))` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:92:9 + | +LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))` + +error: aborting due to 12 previous errors From 2892a2b0f578edd290b3be6f5e5c3280bc160f24 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 24 Sep 2020 23:22:54 +0900 Subject: [PATCH 0629/1110] Fix FP in `print_stdout` This lint shouldn't be emitted in `build.rs` as `println!` and `print!` are used for the build script. --- clippy_lints/src/write.rs | 23 ++++++++++++++++++++--- tests/ui/build.rs | 10 ++++++++++ tests/ui/build.stderr | 0 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/ui/build.rs create mode 100644 tests/ui/build.stderr diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index fac63bcb993..780d474ee96 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::ops::Range; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; +use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; @@ -11,7 +12,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, FileName, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -236,7 +237,15 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + let filename = cx.sess.source_map().span_to_filename(mac.span()); + if_chain! { + if let FileName::Real(filename) = filename; + if let Some(filename) = filename.local_path().file_name(); + if filename != "build.rs"; + then { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + } + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( @@ -251,7 +260,15 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + if_chain! { + let filename = cx.sess.source_map().span_to_filename(mac.span()); + if let FileName::Real(filename) = filename; + if let Some(filename) = filename.local_path().file_name(); + if filename != "build.rs"; + then { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + } + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( diff --git a/tests/ui/build.rs b/tests/ui/build.rs new file mode 100644 index 00000000000..2d43d452a4f --- /dev/null +++ b/tests/ui/build.rs @@ -0,0 +1,10 @@ +#![warn(clippy::print_stdout)] + +fn main() { + // Fix #6041 + // + // The `print_stdout` shouldn't be linted in `build.rs` + // as these methods are used for the build script. + println!("Hello"); + print!("Hello"); +} diff --git a/tests/ui/build.stderr b/tests/ui/build.stderr new file mode 100644 index 00000000000..e69de29bb2d From dc89bb1135afc31fc9ee2272e627192c04354d22 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:21:58 +1200 Subject: [PATCH 0630/1110] Use if_chain in Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 43 +++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7f998c63f49..9f3be26e672 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2162,15 +2162,16 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { if lhs.hir_id == expr.hir_id { - if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) { - *state = match *state { - VarState::Initial if self.depth == 0 => VarState::IncrOnce, - _ => VarState::DontWarn, - }; + *state = if op.node == BinOpKind::Add + && is_integer_const(self.cx, rhs, 1) + && *state == VarState::Initial + && self.depth == 0 + { + VarState::IncrOnce } else { - // Assigned some other value - *state = VarState::DontWarn; - } + // Assigned some other value or assigned multiple times + VarState::DontWarn + }; } }, ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, @@ -2212,18 +2213,20 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { // Look for declarations of the variable - if let StmtKind::Local(ref local) = stmt.kind { - if local.pat.hir_id == self.var_id { - if let PatKind::Binding(.., ident, _) = local.pat.kind { - self.name = Some(ident.name); - - self.state = local.init.as_ref().map_or(VarState::Declared, |init| { - if is_integer_const(&self.cx, init, 0) { - VarState::Warn - } else { - VarState::Declared - } - }) + if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if local.pat.hir_id == self.var_id; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + then { + self.name = Some(ident.name); + self.state = if_chain! { + if let Some(ref init) = local.init; + if is_integer_const(&self.cx, init, 0); + then { + VarState::Warn + } else { + VarState::Declared + } } } } From 116f30dc33d9e3744f257f2f7f5467acfbff178b Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:25:06 +1200 Subject: [PATCH 0631/1110] Use else blocks instead of return statements in Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9f3be26e672..a59d3ef8708 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2181,16 +2181,17 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { _ => (), } } + + walk_expr(self, expr); } else if is_loop(expr) || is_conditional(expr) { self.depth += 1; walk_expr(self, expr); self.depth -= 1; - return; } else if let ExprKind::Continue(_) = expr.kind { self.done = true; - return; + } else { + walk_expr(self, expr); } - walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -2272,16 +2273,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { self.state = VarState::DontWarn; return; } + walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { self.state = VarState::DontWarn; - return; } else if is_conditional(expr) { self.depth += 1; walk_expr(self, expr); self.depth -= 1; - return; + } else { + walk_expr(self, expr); } - walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { From b2d5b89a1de15df9052fdf44d01b174add82837f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:41:09 +1200 Subject: [PATCH 0632/1110] Check if it's after the loop earlier --- clippy_lints/src/loops.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index a59d3ef8708..20e75546d71 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2250,6 +2250,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { // If node is the desired variable, see how it's used if var_def_id(self.cx, expr) == Some(self.var_id) { + if self.past_loop { + self.state = VarState::DontWarn; + return; + } + if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { @@ -2269,10 +2274,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } } - if self.past_loop { - self.state = VarState::DontWarn; - return; - } walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { self.state = VarState::DontWarn; From 31cb1109648bf4242cab47571343578244e7fb9d Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:48:26 +1200 Subject: [PATCH 0633/1110] add concinient methods to Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 63 +++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 20e75546d71..61f0059cca4 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1536,31 +1536,17 @@ fn check_for_loop_explicit_counter<'tcx>( expr: &'tcx Expr<'_>, ) { // Look for variables that are incremented once per loop iteration. - let mut visitor = IncrementVisitor { - cx, - states: FxHashMap::default(), - depth: 0, - done: false, - }; + let mut visitor = IncrementVisitor::new(cx); walk_expr(&mut visitor, body); // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) { - let mut visitor2 = InitializeVisitor { - cx, - end_expr: expr, - var_id: *id, - state: VarState::IncrOnce, - name: None, - depth: 0, - past_loop: false, - }; + for id in visitor.into_results() { + let mut visitor2 = InitializeVisitor::new(cx, expr, id); walk_block(&mut visitor2, block); - if visitor2.state == VarState::Warn { - if let Some(name) = visitor2.name { + if let Some(name) = visitor2.get_result() { let mut applicability = Applicability::MachineApplicable; // for some reason this is the only way to get the `Span` @@ -1585,7 +1571,6 @@ fn check_for_loop_explicit_counter<'tcx>( ), applicability, ); - } } } } @@ -2142,6 +2127,24 @@ struct IncrementVisitor<'a, 'tcx> { done: bool, } +impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + Self { + cx, + states: FxHashMap::default(), + depth: 0, + done: false, + } + } + + fn into_results(self) -> impl Iterator { + self.states + .into_iter() + .filter(|(_, state)| *state == VarState::IncrOnce) + .map(|(id, _)| id) + } +} + impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { type Map = Map<'tcx>; @@ -2209,6 +2212,28 @@ struct InitializeVisitor<'a, 'tcx> { past_loop: bool, } +impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + Self { + cx, + end_expr, + var_id, + state: VarState::IncrOnce, + name: None, + depth: 0, + past_loop: false, + } + } + + fn get_result(&self) -> Option { + if self.state == VarState::Warn { + self.name + } else { + None + } + } +} + impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { type Map = Map<'tcx>; From c599e2fcfaaedb12b560f4136bab3d0b450acf8f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:05:51 +1200 Subject: [PATCH 0634/1110] Split VarState --- clippy_lints/src/loops.rs | 87 +++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 61f0059cca4..2b7830e7cad 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2107,23 +2107,18 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool { } } -// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be -// incremented exactly once in the loop body, and initialized to zero -// at the start of the loop. #[derive(Debug, PartialEq)] -enum VarState { +enum IncrementVisitorVarState { Initial, // Not examined yet IncrOnce, // Incremented exactly once, may be a loop counter - Declared, // Declared but not (yet) initialized to zero - Warn, DontWarn, } /// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, // context reference - states: FxHashMap, // incremented variables - depth: u32, // depth of conditional expressions + cx: &'a LateContext<'tcx>, // context reference + states: FxHashMap, // incremented variables + depth: u32, // depth of conditional expressions done: bool, } @@ -2140,7 +2135,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { fn into_results(self) -> impl Iterator { self.states .into_iter() - .filter(|(_, state)| *state == VarState::IncrOnce) + .filter(|(_, state)| *state == IncrementVisitorVarState::IncrOnce) .map(|(id, _)| id) } } @@ -2156,9 +2151,9 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { // If node is a variable if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { - let state = self.states.entry(def_id).or_insert(VarState::Initial); - if *state == VarState::IncrOnce { - *state = VarState::DontWarn; + let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); + if *state == IncrementVisitorVarState::IncrOnce { + *state = IncrementVisitorVarState::DontWarn; return; } @@ -2167,19 +2162,21 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if lhs.hir_id == expr.hir_id { *state = if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) - && *state == VarState::Initial + && *state == IncrementVisitorVarState::Initial && self.depth == 0 { - VarState::IncrOnce + IncrementVisitorVarState::IncrOnce } else { // Assigned some other value or assigned multiple times - VarState::DontWarn + IncrementVisitorVarState::DontWarn }; } }, - ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, + ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => { + *state = IncrementVisitorVarState::DontWarn + }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - *state = VarState::DontWarn + *state = IncrementVisitorVarState::DontWarn }, _ => (), } @@ -2201,13 +2198,20 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } } -/// Checks whether a variable is initialized to zero at the start of a loop. +enum InitializeVisitorState { + Initial, // Not examined yet + Declared(Symbol), // Declared but not (yet) initialized + Initialized { name: Symbol }, + DontWarn, +} + +/// Checks whether a variable is initialized to zero at the start of a loop and not modified +/// and used after the loop. struct InitializeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, - state: VarState, - name: Option, + state: InitializeVisitorState, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2218,16 +2222,15 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { cx, end_expr, var_id, - state: VarState::IncrOnce, - name: None, + state: InitializeVisitorState::Initial, depth: 0, past_loop: false, } } fn get_result(&self) -> Option { - if self.state == VarState::Warn { - self.name + if let InitializeVisitorState::Initialized { name } = self.state { + Some(name) } else { None } @@ -2244,23 +2247,24 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.name = Some(ident.name); self.state = if_chain! { if let Some(ref init) = local.init; if is_integer_const(&self.cx, init, 0); then { - VarState::Warn - } else { - VarState::Declared + InitializeVisitorState::Initialized { + name: ident.name } + } else { + InitializeVisitorState::Declared(ident.name) } } } + } walk_stmt(self, stmt); } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.state == VarState::DontWarn { + if matches!(self.state, InitializeVisitorState::DontWarn) { return; } if expr.hir_id == self.end_expr.hir_id { @@ -2269,31 +2273,36 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } // No need to visit expressions before the variable is // declared - if self.state == VarState::IncrOnce { + if matches!(self.state, InitializeVisitorState::Initial) { return; } // If node is the desired variable, see how it's used if var_def_id(self.cx, expr) == Some(self.var_id) { if self.past_loop { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; return; } if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; }, ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { - self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 { - VarState::Warn - } else { - VarState::DontWarn + self.state = if_chain! { + if is_integer_const(&self.cx, rhs, 0) && self.depth == 0; + if let InitializeVisitorState::Declared(name) + | InitializeVisitorState::Initialized { name, ..} = self.state; + then { + InitializeVisitorState::Initialized { name } + } else { + InitializeVisitorState::DontWarn + } } }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - self.state = VarState::DontWarn + self.state = InitializeVisitorState::DontWarn }, _ => (), } @@ -2301,7 +2310,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; } else if is_conditional(expr) { self.depth += 1; walk_expr(self, expr); From 13c207d3756754c54a6b20d852087616d5abfbf4 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:14:57 +1200 Subject: [PATCH 0635/1110] Generalise `InitializeVisitor` --- clippy_lints/src/loops.rs | 36 +++++++++++++++++++---------------- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2b7830e7cad..bf067c70a7e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1528,6 +1528,9 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { } } +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be +// incremented exactly once in the loop body, and initialized to zero +// at the start of the loop. fn check_for_loop_explicit_counter<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, @@ -1546,7 +1549,10 @@ fn check_for_loop_explicit_counter<'tcx>( let mut visitor2 = InitializeVisitor::new(cx, expr, id); walk_block(&mut visitor2, block); - if let Some(name) = visitor2.get_result() { + if_chain! { + if let Some((name, initializer)) = visitor2.get_result(); + if is_integer_const(cx, initializer, 0); + then { let mut applicability = Applicability::MachineApplicable; // for some reason this is the only way to get the `Span` @@ -1571,6 +1577,7 @@ fn check_for_loop_explicit_counter<'tcx>( ), applicability, ); + } } } } @@ -2198,20 +2205,20 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } } -enum InitializeVisitorState { +enum InitializeVisitorState<'hir> { Initial, // Not examined yet Declared(Symbol), // Declared but not (yet) initialized - Initialized { name: Symbol }, + Initialized { name: Symbol, initializer: &'hir Expr<'hir> }, DontWarn, } -/// Checks whether a variable is initialized to zero at the start of a loop and not modified +/// Checks whether a variable is initialized at the start of a loop and not modified /// and used after the loop. struct InitializeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, - state: InitializeVisitorState, + state: InitializeVisitorState<'tcx>, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2228,9 +2235,9 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } - fn get_result(&self) -> Option { - if let InitializeVisitorState::Initialized { name } = self.state { - Some(name) + fn get_result(&self) -> Option<(Name, &'tcx Expr<'tcx>)> { + if let InitializeVisitorState::Initialized { name, initializer } = self.state { + Some((name, initializer)) } else { None } @@ -2247,19 +2254,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.state = if_chain! { - if let Some(ref init) = local.init; - if is_integer_const(&self.cx, init, 0); - then { + self.state = if let Some(ref init) = local.init { InitializeVisitorState::Initialized { - name: ident.name + initializer: init, + name: ident.name, } } else { InitializeVisitorState::Declared(ident.name) } } } - } walk_stmt(self, stmt); } @@ -2291,11 +2295,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { }, ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { self.state = if_chain! { - if is_integer_const(&self.cx, rhs, 0) && self.depth == 0; + if self.depth == 0; if let InitializeVisitorState::Declared(name) | InitializeVisitorState::Initialized { name, ..} = self.state; then { - InitializeVisitorState::Initialized { name } + InitializeVisitorState::Initialized { initializer: rhs, name } } else { InitializeVisitorState::DontWarn } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 92cb31fcf85..a0ddcce111e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -707,7 +707,7 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, } /// Gets the parent expression, if any –- this is useful to constrain a lint. -pub fn get_parent_expr<'c>(cx: &'c LateContext<'_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> { +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { let map = &cx.tcx.hir(); let hir_id = e.hir_id; let parent_id = map.get_parent_node(hir_id); From 9573a0d378033a81e55ca834a5d305d3cf2be24d Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:15:20 +1200 Subject: [PATCH 0636/1110] Rename variables --- clippy_lints/src/loops.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index bf067c70a7e..1c316c00f43 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1539,18 +1539,18 @@ fn check_for_loop_explicit_counter<'tcx>( expr: &'tcx Expr<'_>, ) { // Look for variables that are incremented once per loop iteration. - let mut visitor = IncrementVisitor::new(cx); - walk_expr(&mut visitor, body); + let mut increment_visitor = IncrementVisitor::new(cx); + walk_expr(&mut increment_visitor, body); // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for id in visitor.into_results() { - let mut visitor2 = InitializeVisitor::new(cx, expr, id); - walk_block(&mut visitor2, block); + for id in increment_visitor.into_results() { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); + walk_block(&mut initialize_visitor, block); if_chain! { - if let Some((name, initializer)) = visitor2.get_result(); + if let Some((name, initializer)) = initialize_visitor.get_result(); if is_integer_const(cx, initializer, 0); then { let mut applicability = Applicability::MachineApplicable; From 1026b42f0694eb9239b5cebe80be743d5ded0da5 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:32:24 +1200 Subject: [PATCH 0637/1110] Rename a struct and variables --- clippy_lints/src/loops.rs | 65 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c316c00f43..d9896f7fd6b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -820,9 +820,9 @@ impl Offset { } } -struct FixedOffsetVar<'hir> { - var: &'hir Expr<'hir>, - offset: Offset, +struct IndexExpr<'hir> { + base: &'hir Expr<'hir>, + idx_offset: Offset, } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { @@ -845,14 +845,14 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Option { - fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, var: HirId) -> Option { +fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Option { + fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, start: HirId) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, - ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + ExprKind::Path(..) if !same_var(cx, e, start) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), _ => None, } } @@ -860,20 +860,20 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Optio 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) + let offset_opt = if same_var(cx, lhs, start) { + extract_offset(cx, rhs, start) + } else if same_var(cx, rhs, start) { + extract_offset(cx, lhs, start) } else { None }; offset_opt.map(Offset::positive) }, - BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), + BinOpKind::Sub if same_var(cx, lhs, start) => extract_offset(cx, rhs, start).map(Offset::negative), _ => None, }, - ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), + ExprKind::Path(..) if same_var(cx, idx, start) => Some(Offset::positive("0".into())), _ => None, } } @@ -916,8 +916,8 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, - dst_var: FixedOffsetVar<'_>, - src_var: FixedOffsetVar<'_>, + dst: IndexExpr<'_>, + src: IndexExpr<'_>, ) -> String { fn print_sum(arg1: &str, arg2: &Offset) -> String { match (arg1, &arg2.value[..], arg2.sign) { @@ -944,13 +944,13 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { + let print_limit = |end: &Expr<'_>, offset: Offset, base: &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); + if var_def_id(cx, arg) == var_def_id(cx, base); then { match offset.sign { OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), @@ -971,25 +971,26 @@ fn build_manual_memcpy_suggestion<'tcx>( }; 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_offset = print_offset(&start_str, &dst.idx_offset); + let dst_limit = print_limit(end, dst.idx_offset, dst.base); + let src_offset = print_offset(&start_str, &src.idx_offset); + let src_limit = print_limit(end, src.idx_offset, src.base); - 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_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); + let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); let dst = if dst_offset == "" && dst_limit == "" { - dst_var_name + dst_base_str } else { - format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) + format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var_name, src_offset, src_limit + dst, src_base_str, src_offset, src_limit ) } + /// Checks for for loops that sequentially copy items from one slice-like /// object to another. fn detect_manual_memcpy<'tcx>( @@ -1014,18 +1015,18 @@ fn detect_manual_memcpy<'tcx>( 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.typeck_results().expr_ty(seqexpr_left)) - && is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_right)); + if let ExprKind::Index(base_left, idx_left) = lhs.kind; + if let ExprKind::Index(base_right, idx_right) = rhs.kind; + if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) + && is_slice_like(cx, cx.typeck_results().expr_ty(base_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); // Source and destination must be different - if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right); + if var_def_id(cx, base_left) != var_def_id(cx, base_right); then { - Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left }, - FixedOffsetVar { var: seqexpr_right, offset: offset_right })) + Some((IndexExpr { base: base_left, idx_offset: offset_left }, + IndexExpr { base: base_right, idx_offset: offset_right })) } else { None } From b4b4da162f19e9a4c63854a2b5a6167b83f9d8b9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:51:23 +1200 Subject: [PATCH 0638/1110] Introduce Start and StartKind --- clippy_lints/src/loops.rs | 67 +++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d9896f7fd6b..157dff59d44 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -818,13 +818,29 @@ impl Offset { sign: OffsetSign::Positive, } } + + fn empty() -> Self { + Self::positive("0".into()) + } +} + +#[derive(Debug, Clone, Copy)] +enum StartKind<'hir> { + Range, + Counter { initializer: &'hir Expr<'hir> }, } struct IndexExpr<'hir> { base: &'hir Expr<'hir>, + idx: StartKind<'hir>, idx_offset: Offset, } +struct Start<'hir> { + id: HirId, + kind: StartKind<'hir>, +} + fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { let is_slice = match ty.kind() { ty::Ref(_, subty, _) => is_slice_like(cx, subty), @@ -845,14 +861,32 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Option { - fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, start: HirId) -> Option { +fn get_offset<'tcx>( + cx: &LateContext<'tcx>, + idx: &Expr<'_>, + starts: &[Start<'tcx>], +) -> Option<(StartKind<'tcx>, Offset)> { + fn extract_start<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, + starts: &[Start<'tcx>], + ) -> Option> { + starts.iter().find(|var| same_var(cx, expr, var.id)).map(|v| v.kind) + } + + fn extract_offset<'tcx>( + cx: &LateContext<'tcx>, + e: &Expr<'_>, + starts: &[Start<'tcx>], + ) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, - ExprKind::Path(..) if !same_var(cx, e, start) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { + Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())) + }, _ => None, } } @@ -860,20 +894,21 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Opt match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if same_var(cx, lhs, start) { - extract_offset(cx, rhs, start) - } else if same_var(cx, rhs, start) { - extract_offset(cx, lhs, start) + let offset_opt = if let Some(s) = extract_start(cx, lhs, starts) { + extract_offset(cx, rhs, starts).map(|o| (s, o)) + } else if let Some(s) = extract_start(cx, rhs, starts) { + extract_offset(cx, lhs, starts).map(|o| (s, o)) } else { None }; - offset_opt.map(Offset::positive) + offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, - BinOpKind::Sub if same_var(cx, lhs, start) => extract_offset(cx, rhs, start).map(Offset::negative), + BinOpKind::Sub => extract_start(cx, lhs, starts) + .and_then(|s| extract_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))), _ => None, }, - ExprKind::Path(..) if same_var(cx, idx, start) => Some(Offset::positive("0".into())), + ExprKind::Path(..) => extract_start(cx, idx, starts).map(|s| (s, Offset::empty())), _ => None, } } @@ -1008,6 +1043,10 @@ fn detect_manual_memcpy<'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; // The only statements in the for loops can be indexed assignments from // indexed retrievals. let big_sugg = get_assignments(body) @@ -1019,14 +1058,14 @@ fn detect_manual_memcpy<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) && is_slice_like(cx, cx.typeck_results().expr_ty(base_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); + if let Some((start_left, offset_left)) = get_offset(cx, &idx_left, &starts); + if let Some((start_right, offset_right)) = get_offset(cx, &idx_right, &starts); // Source and destination must be different if var_def_id(cx, base_left) != var_def_id(cx, base_right); then { - Some((IndexExpr { base: base_left, idx_offset: offset_left }, - IndexExpr { base: base_right, idx_offset: offset_right })) + Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None } From 720f19f2ec4282f636889b35beabf31272e3b1b2 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:58:12 +1200 Subject: [PATCH 0639/1110] Implement detecting `manual_memcpy` with loop counters --- clippy_lints/src/loops.rs | 90 ++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 157dff59d44..c036d63c335 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -913,37 +913,62 @@ fn get_offset<'tcx>( } } -fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator, &'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 - } +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 } +} - // 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; +fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( + cx: &'a LateContext<'a, 'tcx>, + stmts: &'tcx [Stmt<'tcx>], + expr: Option<&'tcx Expr<'tcx>>, + loop_counters: &'c [Start<'tcx>], +) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { + stmts + .iter() + .filter_map(move |stmt| match stmt.kind { + StmtKind::Local(..) | StmtKind::Item(..) => None, + StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { + if let ExprKind::AssignOp(_, var, _) = e.kind; + // skip StartKind::Range + if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); + then { None } else { Some(e) } + }, + }) + .chain(expr.into_iter()) + .map(get_assignment) +} - if let ExprKind::Block(b, _) = body.kind { - let Block { stmts, expr, .. } = *b; +fn get_loop_counters<'a, 'tcx>( + cx: &'a LateContext<'a, 'tcx>, + body: &'tcx Block<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option> + 'a> { + // Look for variables that are incremented once per loop iteration. + let mut increment_visitor = IncrementVisitor::new(cx); + walk_block(&mut increment_visitor, body); - iter_a = stmts - .iter() - .filter_map(|stmt| match stmt.kind { - StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), + // For each candidate, check the parent block to see if + // it's initialized to zero at the start of the loop. + if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + increment_visitor + .into_results() + .filter_map(move |var_id| { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id); + walk_block(&mut initialize_visitor, block); + + initialize_visitor.get_result().map(|(_, initializer)| Start { + id: var_id, + kind: StartKind::Counter { initializer }, + }) }) - .chain(expr.into_iter()) - .map(get_assignment) .into() } else { - iter_b = Some(get_assignment(body)) + None } - - iter_a.into_iter().flatten().chain(iter_b.into_iter()) } fn build_manual_memcpy_suggestion<'tcx>( @@ -1047,9 +1072,26 @@ fn detect_manual_memcpy<'tcx>( id: canonical_id, kind: StartKind::Range, }]; + + // 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; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); + } + iter_a = Some(get_assignments(cx, block.stmts, block.expr, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } + // The only statements in the for loops can be indexed assignments from // indexed retrievals. - let big_sugg = get_assignments(body) + let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); + + let big_sugg = assignments .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); From de56279cd9047832216e1d1c06dc45375bf01b31 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:06:13 +1200 Subject: [PATCH 0640/1110] Implement building the `manual_memcpy` sugggestion with loop counters --- clippy_lints/src/loops.rs | 185 +++++++++++++++++++++++++-------- clippy_lints/src/utils/sugg.rs | 78 +++++++++++++- 2 files changed, 213 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c036d63c335..bd2ae47b723 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -800,19 +800,19 @@ enum OffsetSign { } struct Offset { - value: String, + value: MinifyingSugg<'static>, sign: OffsetSign, } impl Offset { - fn negative(value: String) -> Self { + fn negative(value: MinifyingSugg<'static>) -> Self { Self { value, sign: OffsetSign::Negative, } } - fn positive(value: String) -> Self { + fn positive(value: MinifyingSugg<'static>) -> Self { Self { value, sign: OffsetSign::Positive, @@ -820,7 +820,88 @@ impl Offset { } fn empty() -> Self { - Self::positive("0".into()) + Self::positive(MinifyingSugg::non_paren("0")) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + +#[derive(Clone)] +struct MinifyingSugg<'a>(sugg::Sugg<'a>); + +impl std::fmt::Display for MinifyingSugg<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Display::fmt(&self.0, f) + } +} + +impl<'a> MinifyingSugg<'a> { + fn as_str(&self) -> &str { + let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0; + s.as_ref() + } + + fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self { + Self(sugg::Sugg::hir(cx, expr, default)) + } + + fn maybe_par(self) -> Self { + Self(self.0.maybe_par()) + } + + fn non_paren(str: impl Into>) -> Self { + Self(sugg::Sugg::NonParen(str.into())) + } +} + +impl std::ops::Add for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self.clone(), + (_, _) => MinifyingSugg(&self.0 + &rhs.0), + } + } +} + +impl std::ops::Sub for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self.clone(), + ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + (x, y) if x == y => MinifyingSugg::non_paren("0"), + (_, _) => MinifyingSugg(&self.0 - &rhs.0), + } + } +} + +impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self, + (_, _) => MinifyingSugg(self.0 + &rhs.0), + } + } +} + +impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self, + ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + (x, y) if x == y => MinifyingSugg::non_paren("0"), + (_, _) => MinifyingSugg(self.0 - &rhs.0), + } } } @@ -878,14 +959,15 @@ fn get_offset<'tcx>( cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>], - ) -> Option { + ) -> Option> { match &e.kind { ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(x.to_string()), + ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), _ => None, }, ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { - Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())) + // `e` is always non paren as it's a `Path` + Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) }, _ => None, } @@ -979,32 +1061,15 @@ fn build_manual_memcpy_suggestion<'tcx>( dst: IndexExpr<'_>, src: IndexExpr<'_>, ) -> 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); + fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { - "".into() + MinifyingSugg::non_paren("") } else { offset } } - let print_limit = |end: &Expr<'_>, offset: Offset, base: &Expr<'_>| { + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { if_chain! { if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; if method.ident.name == sym!(len); @@ -1012,42 +1077,72 @@ fn build_manual_memcpy_suggestion<'tcx>( if let Some(arg) = len_args.get(0); if var_def_id(cx, arg) == var_def_id(cx, base); then { - match offset.sign { - OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), - OffsetSign::Positive => "".into(), + if sugg.as_str() == end_str { + MinifyingSugg::non_paren("") + } else { + sugg } } else { - let end_str = match limits { + match limits { ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) + sugg + &MinifyingSugg::non_paren("1") }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&end_str, &offset) + ast::RangeLimits::HalfOpen => sugg, + } } } }; - let start_str = snippet(cx, start.span, "").to_string(); - let dst_offset = print_offset(&start_str, &dst.idx_offset); - let dst_limit = print_limit(end, dst.idx_offset, dst.base); - let src_offset = print_offset(&start_str, &src.idx_offset); - let src_limit = print_limit(end, src.idx_offset, src.base); + let start_str = MinifyingSugg::hir(cx, start, ""); + let end_str = MinifyingSugg::hir(cx, end, ""); + + let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { + StartKind::Range => ( + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset), + ), + ), + StartKind::Counter { initializer } => { + let counter_start = MinifyingSugg::hir(cx, initializer, ""); + ( + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, + ), + ) + }, + }; + + let (dst_offset, dst_limit) = print_offset_and_limit(&dst); + let (src_offset, src_limit) = print_offset_and_limit(&src); let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); - let dst = if dst_offset == "" && dst_limit == "" { + let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { dst_base_str } else { - format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit) + format!( + "{}[{}..{}]", + dst_base_str, + dst_offset.maybe_par(), + dst_limit.maybe_par() + ) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_base_str, src_offset, src_limit + dst, + src_base_str, + src_offset.maybe_par(), + src_limit.maybe_par() ) } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index ec8b7e59b59..50d48650a09 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -36,6 +36,46 @@ impl Display for Sugg<'_> { } } +// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp +impl Clone for Sugg<'_> { + fn clone(&self) -> Self { + /// manually cloning AssocOp + fn clone_assoc_op(this: &AssocOp) -> AssocOp { + match this { + AssocOp::Add => AssocOp::Add, + AssocOp::Subtract => AssocOp::Subtract, + AssocOp::Multiply => AssocOp::Multiply, + AssocOp::Divide => AssocOp::Divide, + AssocOp::Modulus => AssocOp::Modulus, + AssocOp::LAnd => AssocOp::LAnd, + AssocOp::LOr => AssocOp::LOr, + AssocOp::BitXor => AssocOp::BitXor, + AssocOp::BitAnd => AssocOp::BitAnd, + AssocOp::BitOr => AssocOp::BitOr, + AssocOp::ShiftLeft => AssocOp::ShiftLeft, + AssocOp::ShiftRight => AssocOp::ShiftRight, + AssocOp::Equal => AssocOp::Equal, + AssocOp::Less => AssocOp::Less, + AssocOp::LessEqual => AssocOp::LessEqual, + AssocOp::NotEqual => AssocOp::NotEqual, + AssocOp::Greater => AssocOp::Greater, + AssocOp::GreaterEqual => AssocOp::GreaterEqual, + AssocOp::Assign => AssocOp::Assign, + AssocOp::AssignOp(t) => AssocOp::AssignOp(*t), + AssocOp::As => AssocOp::As, + AssocOp::DotDot => AssocOp::DotDot, + AssocOp::DotDotEq => AssocOp::DotDotEq, + AssocOp::Colon => AssocOp::Colon, + } + } + match self { + Sugg::NonParen(x) => Sugg::NonParen(x.clone()), + Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()), + Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()), + } + } +} + #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. @@ -267,21 +307,49 @@ impl<'a> Sugg<'a> { } } -impl<'a, 'b> std::ops::Add> for Sugg<'a> { +impl std::ops::Add for Sugg<'_> { type Output = Sugg<'static>; - fn add(self, rhs: Sugg<'b>) -> Sugg<'static> { + fn add(self, rhs: Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Add, &self, &rhs) } } -impl<'a, 'b> std::ops::Sub> for Sugg<'a> { +impl std::ops::Sub for Sugg<'_> { type Output = Sugg<'static>; - fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> { + fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Sub, &self, &rhs) } } -impl<'a> std::ops::Not for Sugg<'a> { +impl std::ops::Add<&Sugg<'_>> for Sugg<'_> { + type Output = Sugg<'static>; + fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, &self, rhs) + } +} + +impl std::ops::Sub<&Sugg<'_>> for Sugg<'_> { + type Output = Sugg<'static>; + fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, &self, rhs) + } +} + +impl std::ops::Add for &Sugg<'_> { + type Output = Sugg<'static>; + fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, self, rhs) + } +} + +impl std::ops::Sub for &Sugg<'_> { + type Output = Sugg<'static>; + fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, self, rhs) + } +} + +impl std::ops::Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { make_unop("!", self) From 8da6cfd17b7707d678d01a6688572169015ea83e Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 12:11:06 +1200 Subject: [PATCH 0641/1110] fmt --- clippy_lints/src/loops.rs | 41 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index bd2ae47b723..2aee3b93e82 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1069,29 +1069,30 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { - 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, base); - then { - if sugg.as_str() == end_str { - MinifyingSugg::non_paren("") + let print_limit = + |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { + 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, base); + then { + if sugg.as_str() == end_str { + MinifyingSugg::non_paren("") + } else { + sugg + } } else { - sugg - } - } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &MinifyingSugg::non_paren("1") - }, - ast::RangeLimits::HalfOpen => sugg, + match limits { + ast::RangeLimits::Closed => { + sugg + &MinifyingSugg::non_paren("1") + }, + ast::RangeLimits::HalfOpen => sugg, + } } } - } - }; + }; let start_str = MinifyingSugg::hir(cx, start, ""); let end_str = MinifyingSugg::hir(cx, end, ""); From d9a88be0b05c2cfec0679caf428d129c9d46256d Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:24:12 +1200 Subject: [PATCH 0642/1110] Rename `get_offset` and its private items --- clippy_lints/src/loops.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2aee3b93e82..98c411f5ae6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -942,20 +942,20 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>( +fn get_details_from_idx<'tcx>( cx: &LateContext<'tcx>, idx: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { - fn extract_start<'tcx>( + fn get_start<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + e: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option> { - starts.iter().find(|var| same_var(cx, expr, var.id)).map(|v| v.kind) + starts.iter().find(|var| same_var(cx, e, var.id)).map(|v| v.kind) } - fn extract_offset<'tcx>( + fn get_offset<'tcx>( cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>], @@ -965,7 +965,7 @@ fn get_offset<'tcx>( ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), _ => None, }, - ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { + ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { // `e` is always non paren as it's a `Path` Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) }, @@ -976,21 +976,22 @@ fn get_offset<'tcx>( match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if let Some(s) = extract_start(cx, lhs, starts) { - extract_offset(cx, rhs, starts).map(|o| (s, o)) - } else if let Some(s) = extract_start(cx, rhs, starts) { - extract_offset(cx, lhs, starts).map(|o| (s, o)) + let offset_opt = if let Some(s) = get_start(cx, lhs, starts) { + get_offset(cx, rhs, starts).map(|o| (s, o)) + } else if let Some(s) = get_start(cx, rhs, starts) { + get_offset(cx, lhs, starts).map(|o| (s, o)) } else { None }; offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, - BinOpKind::Sub => extract_start(cx, lhs, starts) - .and_then(|s| extract_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))), + BinOpKind::Sub => { + get_start(cx, lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))) + }, _ => None, }, - ExprKind::Path(..) => extract_start(cx, idx, starts).map(|s| (s, Offset::empty())), + ExprKind::Path(..) => get_start(cx, idx, starts).map(|s| (s, Offset::empty())), _ => None, } } @@ -1196,8 +1197,8 @@ fn detect_manual_memcpy<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - if let Some((start_left, offset_left)) = get_offset(cx, &idx_left, &starts); - if let Some((start_right, offset_right)) = get_offset(cx, &idx_right, &starts); + if let Some((start_left, offset_left)) = get_details_from_idx(cx, &idx_left, &starts); + if let Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts); // Source and destination must be different if var_def_id(cx, base_left) != var_def_id(cx, base_right); From 774e82a566c6c3349700a8bece44d6a8c6755039 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:19:44 +1200 Subject: [PATCH 0643/1110] Add the tests for `manual_memcpy` with loop counters --- tests/ui/manual_memcpy.rs | 54 +++++++++++++++++++++++++++++++ tests/ui/manual_memcpy.stderr | 60 +++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 0083f94798f..87bfb4fdd16 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,6 +115,60 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } +#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..(3 + src.len()) { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + for i in 5..src.len() { + dst[i] = src[count - 2]; + count += 1; + } + + let mut count = 5; + for i in 3..10 { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + let mut count = 30; + for i in 0..src.len() { + dst[count] = src[i]; + dst2[count] = src[i]; + count += 1; + count += 1; + } +} + #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] pub fn manual_clone(src: &[String], dst: &mut [String]) { for i in 0..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index bad84a58900..a5698b08103 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -16,7 +16,7 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:17:14 | LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])` + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:22:14 @@ -79,10 +79,64 @@ 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 + --> $DIR/manual_memcpy.rs:121:14 + | +LL | for i in 3..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:127:14 + | +LL | for i in 3..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:133:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:139:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:145:14 + | +LL | for i in 3..(3 + src.len()) { + | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:151:14 + | +LL | for i in 5..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:157:14 + | +LL | for i in 3..10 { + | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:164:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ + | +help: try replacing the loop by + | +LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { + | + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:174: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 13 previous errors +error: aborting due to 21 previous errors From 9aad38bf614c3fb6d306f5dec4a0af606bb3c9c8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:48:44 +1200 Subject: [PATCH 0644/1110] Update `manual_memcpy.stderr` to reflect additional parentheses --- tests/ui/manual_memcpy.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index a5698b08103..e6bef9981a3 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -58,13 +58,13 @@ 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[..(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[..(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:103:14 @@ -106,7 +106,7 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:145:14 | LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)])` + | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:151:14 From 4ea4a972500a8ddecfc737d51eec960324dcb02f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 12:31:13 +1200 Subject: [PATCH 0645/1110] Add tests for bitwise operations --- tests/ui/manual_memcpy.rs | 8 ++++++++ tests/ui/manual_memcpy.stderr | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 87bfb4fdd16..4846ab5eaaa 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -167,6 +167,14 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) count += 1; count += 1; } + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetric ones + let mut count = 0 << 1; + for i in 0..1 << 1 { + dst[count] = src[i + 2]; + count += 1; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index e6bef9981a3..d189bde0508 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -135,8 +135,14 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:174:14 | +LL | for i in 0..1 << 1 { + | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:182: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 21 previous errors +error: aborting due to 22 previous errors From eb3ffe6ed2999e9d3385be0ff981e30082ea0d2c Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 23 Jun 2020 18:51:05 +1200 Subject: [PATCH 0646/1110] make use of macros in operator overloading --- clippy_lints/src/utils/sugg.rs | 61 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 50d48650a09..aada4122e78 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,6 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; +use std::ops::{Add, Sub, Not}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -307,49 +308,53 @@ impl<'a> Sugg<'a> { } } -impl std::ops::Add for Sugg<'_> { - type Output = Sugg<'static>; - fn add(self, rhs: Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Add, &self, &rhs) +// Copied from the rust standart library, and then edited +macro_rules! forward_binop_impls_to_ref { + (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { + impl $imp<$t> for &$t { + type Output = $o; + + fn $method(self, other: $t) -> $o { + $imp::$method(self, &other) + } + } + + impl $imp<&$t> for $t { + type Output = $o; + + fn $method(self, other: &$t) -> $o { + $imp::$method(&self, other) + } + } + + impl $imp for $t { + type Output = $o; + + fn $method(self, other: $t) -> $o { + $imp::$method(&self, &other) + } + } } } -impl std::ops::Sub for Sugg<'_> { - type Output = Sugg<'static>; - fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Sub, &self, &rhs) - } -} - -impl std::ops::Add<&Sugg<'_>> for Sugg<'_> { - type Output = Sugg<'static>; - fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Add, &self, rhs) - } -} - -impl std::ops::Sub<&Sugg<'_>> for Sugg<'_> { - type Output = Sugg<'static>; - fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Sub, &self, rhs) - } -} - -impl std::ops::Add for &Sugg<'_> { +impl Add for &Sugg<'_> { type Output = Sugg<'static>; fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Add, self, rhs) } } -impl std::ops::Sub for &Sugg<'_> { +impl Sub for &Sugg<'_> { type Output = Sugg<'static>; fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Sub, self, rhs) } } -impl std::ops::Not for Sugg<'_> { +forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); +forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); + +impl Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { make_unop("!", self) From 10d7a18f72155f03dbd27b872a52b5dd45def8db Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 23 Jun 2020 22:37:36 +1200 Subject: [PATCH 0647/1110] fmt --- clippy_lints/src/utils/sugg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index aada4122e78..8f2aa724d20 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,7 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use std::ops::{Add, Sub, Not}; +use std::ops::{Add, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -334,7 +334,7 @@ macro_rules! forward_binop_impls_to_ref { $imp::$method(&self, &other) } } - } + }; } impl Add for &Sugg<'_> { From 174065fc98ef9335ea45a234aa18286cdf6c3934 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:33:49 +1200 Subject: [PATCH 0648/1110] fix the multiple counters test --- tests/ui/manual_memcpy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 4846ab5eaaa..8318fd89811 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -160,12 +160,12 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) } let mut count = 3; - let mut count = 30; + let mut count2 = 30; for i in 0..src.len() { dst[count] = src[i]; - dst2[count] = src[i]; - count += 1; + dst2[count2] = src[i]; count += 1; + count2 += 1; } // make sure parentheses are added properly to bitwise operators, which have lower precedence than From 44187383f4724bd7e4b2b220235e93438043947a Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 24 Jun 2020 12:41:46 +1200 Subject: [PATCH 0649/1110] Use operator overloading instead of direct calls of `make_binop` --- clippy_lints/src/loops.rs | 4 ++-- clippy_lints/src/utils/sugg.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 98c411f5ae6..5928d12f30b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -875,7 +875,7 @@ impl std::ops::Sub for &MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self.clone(), - ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + ("0", _) => MinifyingSugg(-(rhs.0.clone())), (x, y) if x == y => MinifyingSugg::non_paren("0"), (_, _) => MinifyingSugg(&self.0 - &rhs.0), } @@ -898,7 +898,7 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self, - ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + ("0", _) => MinifyingSugg(-(rhs.0.clone())), (x, y) if x == y => MinifyingSugg::non_paren("0"), (_, _) => MinifyingSugg(self.0 - &rhs.0), } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 8f2aa724d20..1f448f86d72 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,7 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use std::ops::{Add, Not, Sub}; +use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -354,6 +354,13 @@ impl Sub for &Sugg<'_> { forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); +impl Neg for Sugg<'_> { + type Output = Sugg<'static>; + fn neg(self) -> Sugg<'static> { + make_unop("-", self) + } +} + impl Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { From f410df3c4810e80b6703dcfdbc4d48f812eb4889 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sat, 27 Jun 2020 18:56:38 +1200 Subject: [PATCH 0650/1110] make clippy happy (`needless_pass_by_value`, `filter_map` and `find_map`) --- clippy_lints/src/loops.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5928d12f30b..f684e7de8f8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -952,7 +952,13 @@ fn get_details_from_idx<'tcx>( e: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option> { - starts.iter().find(|var| same_var(cx, e, var.id)).map(|v| v.kind) + starts.iter().find_map(|start| { + if same_var(cx, e, start.id) { + Some(start.kind) + } else { + None + } + }) } fn get_offset<'tcx>( @@ -1059,8 +1065,8 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, - dst: IndexExpr<'_>, - src: IndexExpr<'_>, + dst: &IndexExpr<'_>, + src: &IndexExpr<'_>, ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { @@ -1211,7 +1217,7 @@ fn detect_manual_memcpy<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src))) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) .collect::>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -2319,10 +2325,13 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { } fn into_results(self) -> impl Iterator { - self.states - .into_iter() - .filter(|(_, state)| *state == IncrementVisitorVarState::IncrOnce) - .map(|(id, _)| id) + self.states.into_iter().filter_map(|(id, state)| { + if state == IncrementVisitorVarState::IncrOnce { + Some(id) + } else { + None + } + }) } } From ce653d65a8f08d785c5061e26570b4e59372e325 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 1 Jul 2020 12:22:16 +1200 Subject: [PATCH 0651/1110] use `#[derive]` instead of the manual implementation --- clippy_lints/src/utils/sugg.rs | 41 +--------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 1f448f86d72..062b273c0f4 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -16,6 +16,7 @@ use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. +#[derive(Clone)] pub enum Sugg<'a> { /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -37,46 +38,6 @@ impl Display for Sugg<'_> { } } -// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp -impl Clone for Sugg<'_> { - fn clone(&self) -> Self { - /// manually cloning AssocOp - fn clone_assoc_op(this: &AssocOp) -> AssocOp { - match this { - AssocOp::Add => AssocOp::Add, - AssocOp::Subtract => AssocOp::Subtract, - AssocOp::Multiply => AssocOp::Multiply, - AssocOp::Divide => AssocOp::Divide, - AssocOp::Modulus => AssocOp::Modulus, - AssocOp::LAnd => AssocOp::LAnd, - AssocOp::LOr => AssocOp::LOr, - AssocOp::BitXor => AssocOp::BitXor, - AssocOp::BitAnd => AssocOp::BitAnd, - AssocOp::BitOr => AssocOp::BitOr, - AssocOp::ShiftLeft => AssocOp::ShiftLeft, - AssocOp::ShiftRight => AssocOp::ShiftRight, - AssocOp::Equal => AssocOp::Equal, - AssocOp::Less => AssocOp::Less, - AssocOp::LessEqual => AssocOp::LessEqual, - AssocOp::NotEqual => AssocOp::NotEqual, - AssocOp::Greater => AssocOp::Greater, - AssocOp::GreaterEqual => AssocOp::GreaterEqual, - AssocOp::Assign => AssocOp::Assign, - AssocOp::AssignOp(t) => AssocOp::AssignOp(*t), - AssocOp::As => AssocOp::As, - AssocOp::DotDot => AssocOp::DotDot, - AssocOp::DotDotEq => AssocOp::DotDotEq, - AssocOp::Colon => AssocOp::Colon, - } - } - match self { - Sugg::NonParen(x) => Sugg::NonParen(x.clone()), - Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()), - Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()), - } - } -} - #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. From e855fe3620c0a6981a4238df548fa5c2f36a24c9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 16 Aug 2020 14:01:56 +1200 Subject: [PATCH 0652/1110] Reflect the changes that has been made and fmt --- clippy_lints/src/loops.rs | 45 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f684e7de8f8..4ff567ffb0e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -846,7 +846,7 @@ impl<'a> MinifyingSugg<'a> { s.as_ref() } - fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self { + fn hir(cx: &LateContext<'_>, expr: &Expr<'_>, default: &'a str) -> Self { Self(sugg::Sugg::hir(cx, expr, default)) } @@ -947,11 +947,7 @@ fn get_details_from_idx<'tcx>( idx: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { - fn get_start<'tcx>( - cx: &LateContext<'tcx>, - e: &Expr<'_>, - starts: &[Start<'tcx>], - ) -> Option> { + fn get_start<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { starts.iter().find_map(|start| { if same_var(cx, e, start.id) { Some(start.kind) @@ -982,13 +978,9 @@ fn get_details_from_idx<'tcx>( match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if let Some(s) = get_start(cx, lhs, starts) { - get_offset(cx, rhs, starts).map(|o| (s, o)) - } else if let Some(s) = get_start(cx, rhs, starts) { - get_offset(cx, lhs, starts).map(|o| (s, o)) - } else { - None - }; + let offset_opt = get_start(cx, lhs, starts) + .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o))) + .or_else(|| get_start(cx, rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o)))); offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, @@ -1011,7 +1003,7 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx } fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( - cx: &'a LateContext<'a, 'tcx>, + cx: &'a LateContext<'tcx>, stmts: &'tcx [Stmt<'tcx>], expr: Option<&'tcx Expr<'tcx>>, loop_counters: &'c [Start<'tcx>], @@ -1032,7 +1024,7 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( } fn get_loop_counters<'a, 'tcx>( - cx: &'a LateContext<'a, 'tcx>, + cx: &'a LateContext<'tcx>, body: &'tcx Block<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> + 'a> { @@ -1042,7 +1034,7 @@ fn get_loop_counters<'a, 'tcx>( // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. - if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + get_enclosing_block(&cx, expr.hir_id).and_then(|block| { increment_visitor .into_results() .filter_map(move |var_id| { @@ -1055,9 +1047,7 @@ fn get_loop_counters<'a, 'tcx>( }) }) .into() - } else { - None - } + }) } fn build_manual_memcpy_suggestion<'tcx>( @@ -2315,7 +2305,7 @@ struct IncrementVisitor<'a, 'tcx> { } impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, states: FxHashMap::default(), @@ -2396,7 +2386,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { enum InitializeVisitorState<'hir> { Initial, // Not examined yet Declared(Symbol), // Declared but not (yet) initialized - Initialized { name: Symbol, initializer: &'hir Expr<'hir> }, + Initialized { + name: Symbol, + initializer: &'hir Expr<'hir>, + }, DontWarn, } @@ -2412,7 +2405,7 @@ struct InitializeVisitor<'a, 'tcx> { } impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'a, 'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { Self { cx, end_expr, @@ -2423,7 +2416,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } - fn get_result(&self) -> Option<(Name, &'tcx Expr<'tcx>)> { + fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> { if let InitializeVisitorState::Initialized { name, initializer } = self.state { Some((name, initializer)) } else { @@ -2442,14 +2435,12 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.state = if let Some(ref init) = local.init { + self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| { InitializeVisitorState::Initialized { initializer: init, name: ident.name, } - } else { - InitializeVisitorState::Declared(ident.name) - } + }) } } walk_stmt(self, stmt); From 6b59675449d659123718e7d766432caa1ae0a0aa Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 25 Sep 2020 15:19:36 +0200 Subject: [PATCH 0653/1110] Remove run-pass annotations from crash tests It does not seem to be necessary --- tests/ui/crashes/associated-constant-ice.rs | 2 -- tests/ui/crashes/cc_seme.rs | 2 -- tests/ui/crashes/enum-glob-import-crate.rs | 2 -- tests/ui/crashes/ice-1588.rs | 2 -- tests/ui/crashes/ice-1782.rs | 2 -- tests/ui/crashes/ice-1969.rs | 2 -- tests/ui/crashes/ice-2499.rs | 2 -- tests/ui/crashes/ice-2594.rs | 2 -- tests/ui/crashes/ice-2727.rs | 2 -- tests/ui/crashes/ice-2760.rs | 2 -- tests/ui/crashes/ice-2774.rs | 2 -- tests/ui/crashes/ice-2862.rs | 2 -- tests/ui/crashes/ice-2865.rs | 2 -- tests/ui/crashes/ice-3151.rs | 2 -- tests/ui/crashes/ice-3462.rs | 2 -- tests/ui/crashes/ice-3741.rs | 1 - tests/ui/crashes/ice-3747.rs | 2 -- tests/ui/crashes/ice-4727.rs | 2 -- tests/ui/crashes/ice-4760.rs | 1 - tests/ui/crashes/ice-700.rs | 2 -- tests/ui/crashes/ice_exacte_size.rs | 2 -- tests/ui/crashes/if_same_then_else.rs | 2 -- tests/ui/crashes/issue-825.rs | 2 -- tests/ui/crashes/issues_loop_mut_cond.rs | 2 -- tests/ui/crashes/match_same_arms_const.rs | 2 -- tests/ui/crashes/mut_mut_macro.rs | 2 -- tests/ui/crashes/needless_borrow_fp.rs | 2 -- tests/ui/crashes/needless_lifetimes_impl_trait.rs | 2 -- tests/ui/crashes/procedural_macro.rs | 2 -- tests/ui/crashes/regressions.rs | 2 -- tests/ui/crashes/returns.rs | 2 -- tests/ui/crashes/single-match-else.rs | 2 -- tests/ui/crashes/trivial_bounds.rs | 2 -- tests/ui/crashes/used_underscore_binding_macro.rs | 2 -- 34 files changed, 66 deletions(-) diff --git a/tests/ui/crashes/associated-constant-ice.rs b/tests/ui/crashes/associated-constant-ice.rs index 4bb833795bb..948deba3ea6 100644 --- a/tests/ui/crashes/associated-constant-ice.rs +++ b/tests/ui/crashes/associated-constant-ice.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1698 pub trait Trait { diff --git a/tests/ui/crashes/cc_seme.rs b/tests/ui/crashes/cc_seme.rs index c48c7e9e6c6..98588be9cf8 100644 --- a/tests/ui/crashes/cc_seme.rs +++ b/tests/ui/crashes/cc_seme.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/478 diff --git a/tests/ui/crashes/enum-glob-import-crate.rs b/tests/ui/crashes/enum-glob-import-crate.rs index db1fa871afe..dca32aa3b56 100644 --- a/tests/ui/crashes/enum-glob-import-crate.rs +++ b/tests/ui/crashes/enum-glob-import-crate.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] #![allow(unused_imports)] diff --git a/tests/ui/crashes/ice-1588.rs b/tests/ui/crashes/ice-1588.rs index 15d0f705b36..b0a3d11bce4 100644 --- a/tests/ui/crashes/ice-1588.rs +++ b/tests/ui/crashes/ice-1588.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index 1ca6b6976b3..81af88962a6 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/crashes/ice-1969.rs b/tests/ui/crashes/ice-1969.rs index 837ec9df31a..96a8fe6c24d 100644 --- a/tests/ui/crashes/ice-1969.rs +++ b/tests/ui/crashes/ice-1969.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1969 diff --git a/tests/ui/crashes/ice-2499.rs b/tests/ui/crashes/ice-2499.rs index ffef1631775..45b3b1869dd 100644 --- a/tests/ui/crashes/ice-2499.rs +++ b/tests/ui/crashes/ice-2499.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2594.rs b/tests/ui/crashes/ice-2594.rs index ac19f1976e9..3f3986b6fc6 100644 --- a/tests/ui/crashes/ice-2594.rs +++ b/tests/ui/crashes/ice-2594.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2727.rs b/tests/ui/crashes/ice-2727.rs index d832c286033..56024abc8f5 100644 --- a/tests/ui/crashes/ice-2727.rs +++ b/tests/ui/crashes/ice-2727.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2727 pub fn f(new: fn()) { diff --git a/tests/ui/crashes/ice-2760.rs b/tests/ui/crashes/ice-2760.rs index 9e5e299c336..f1a229f3f4f 100644 --- a/tests/ui/crashes/ice-2760.rs +++ b/tests/ui/crashes/ice-2760.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow( unused_variables, clippy::blacklisted_name, diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs index 47f8e3b18ee..d44b0fae820 100644 --- a/tests/ui/crashes/ice-2774.rs +++ b/tests/ui/crashes/ice-2774.rs @@ -1,5 +1,3 @@ -// run-pass - use std::collections::HashSet; // See rust-lang/rust-clippy#2774. diff --git a/tests/ui/crashes/ice-2862.rs b/tests/ui/crashes/ice-2862.rs index 47324ce1831..8326e3663b0 100644 --- a/tests/ui/crashes/ice-2862.rs +++ b/tests/ui/crashes/ice-2862.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2862 pub trait FooMap { diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index c4f6c0fed68..6b1ceb50569 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index ffad2d06b56..fef4d7db84d 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 #[derive(Clone)] diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs index 95c7dff9be3..7d62e315da2 100644 --- a/tests/ui/crashes/ice-3462.rs +++ b/tests/ui/crashes/ice-3462.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::all)] #![allow(clippy::blacklisted_name)] #![allow(unused)] diff --git a/tests/ui/crashes/ice-3741.rs b/tests/ui/crashes/ice-3741.rs index a548415da62..1253ddcfaeb 100644 --- a/tests/ui/crashes/ice-3741.rs +++ b/tests/ui/crashes/ice-3741.rs @@ -1,5 +1,4 @@ // aux-build:proc_macro_crash.rs -// run-pass #![warn(clippy::suspicious_else_formatting)] diff --git a/tests/ui/crashes/ice-3747.rs b/tests/ui/crashes/ice-3747.rs index d0b44ebafee..cdf018cbc88 100644 --- a/tests/ui/crashes/ice-3747.rs +++ b/tests/ui/crashes/ice-3747.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/3747 macro_rules! a { diff --git a/tests/ui/crashes/ice-4727.rs b/tests/ui/crashes/ice-4727.rs index cdb59caec67..2a4bc83f58a 100644 --- a/tests/ui/crashes/ice-4727.rs +++ b/tests/ui/crashes/ice-4727.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::use_self)] #[path = "auxiliary/ice-4727-aux.rs"] diff --git a/tests/ui/crashes/ice-4760.rs b/tests/ui/crashes/ice-4760.rs index ead67d5ed1b..08b06961760 100644 --- a/tests/ui/crashes/ice-4760.rs +++ b/tests/ui/crashes/ice-4760.rs @@ -1,4 +1,3 @@ -// run-pass const COUNT: usize = 2; struct Thing; trait Dummy {} diff --git a/tests/ui/crashes/ice-700.rs b/tests/ui/crashes/ice-700.rs index b06df83d51a..0cbceedbd6b 100644 --- a/tests/ui/crashes/ice-700.rs +++ b/tests/ui/crashes/ice-700.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/700 diff --git a/tests/ui/crashes/ice_exacte_size.rs b/tests/ui/crashes/ice_exacte_size.rs index e02eb28ab86..30e4b11ec0b 100644 --- a/tests/ui/crashes/ice_exacte_size.rs +++ b/tests/ui/crashes/ice_exacte_size.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1336 diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 4ef992b05e7..2f913292995 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] diff --git a/tests/ui/crashes/issue-825.rs b/tests/ui/crashes/issue-825.rs index 3d4a88ab3c4..05696e3d7d5 100644 --- a/tests/ui/crashes/issue-825.rs +++ b/tests/ui/crashes/issue-825.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(warnings)] /// Test for https://github.com/rust-lang/rust-clippy/issues/825 diff --git a/tests/ui/crashes/issues_loop_mut_cond.rs b/tests/ui/crashes/issues_loop_mut_cond.rs index c4acd5cda1b..bb238c81ebc 100644 --- a/tests/ui/crashes/issues_loop_mut_cond.rs +++ b/tests/ui/crashes/issues_loop_mut_cond.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code)] /// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 diff --git a/tests/ui/crashes/match_same_arms_const.rs b/tests/ui/crashes/match_same_arms_const.rs index 848f0ea52ca..94c939665e6 100644 --- a/tests/ui/crashes/match_same_arms_const.rs +++ b/tests/ui/crashes/match_same_arms_const.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::match_same_arms)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2427 diff --git a/tests/ui/crashes/mut_mut_macro.rs b/tests/ui/crashes/mut_mut_macro.rs index d8fbaa54146..a238e7896fc 100644 --- a/tests/ui/crashes/mut_mut_macro.rs +++ b/tests/ui/crashes/mut_mut_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] #![allow(dead_code)] diff --git a/tests/ui/crashes/needless_borrow_fp.rs b/tests/ui/crashes/needless_borrow_fp.rs index 48507efe1e9..4f61c76828d 100644 --- a/tests/ui/crashes/needless_borrow_fp.rs +++ b/tests/ui/crashes/needless_borrow_fp.rs @@ -1,5 +1,3 @@ -// run-pass - #[deny(clippy::all)] #[derive(Debug)] pub enum Error { diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs index bd1fa4a0b1e..676564b2445 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::needless_lifetimes)] #![allow(dead_code)] diff --git a/tests/ui/crashes/procedural_macro.rs b/tests/ui/crashes/procedural_macro.rs index f79d9ab6460..c7468493380 100644 --- a/tests/ui/crashes/procedural_macro.rs +++ b/tests/ui/crashes/procedural_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #[macro_use] extern crate clippy_mini_macro_test; diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 3d5063d1a3a..a41bcb33b44 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::blacklisted_name)] pub fn foo(bar: *const u8) { diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs index f2153efc388..8021ed4607d 100644 --- a/tests/ui/crashes/returns.rs +++ b/tests/ui/crashes/returns.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1346 #[deny(warnings)] diff --git a/tests/ui/crashes/single-match-else.rs b/tests/ui/crashes/single-match-else.rs index 3a4bbe310cc..1ba7ac08213 100644 --- a/tests/ui/crashes/single-match-else.rs +++ b/tests/ui/crashes/single-match-else.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::single_match_else)] //! Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/trivial_bounds.rs b/tests/ui/crashes/trivial_bounds.rs index 2bb95c18a39..60105a8213f 100644 --- a/tests/ui/crashes/trivial_bounds.rs +++ b/tests/ui/crashes/trivial_bounds.rs @@ -1,5 +1,3 @@ -// run-pass - #![feature(trivial_bounds)] #![allow(unused, trivial_bounds)] diff --git a/tests/ui/crashes/used_underscore_binding_macro.rs b/tests/ui/crashes/used_underscore_binding_macro.rs index 265017c51d9..6d2124c12fe 100644 --- a/tests/ui/crashes/used_underscore_binding_macro.rs +++ b/tests/ui/crashes/used_underscore_binding_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::useless_attribute)] //issue #2910 #[macro_use] From fd0656109fb7317266e372bd5cc5c4c10299f825 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 25 Sep 2020 15:20:04 +0200 Subject: [PATCH 0654/1110] Add emit=metadata to UI tests build flags This should improve the performance by avoiding codegen --- tests/compile-test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 697823712bf..f0d73e9b0e2 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -71,7 +71,7 @@ fn default_config() -> compiletest::Config { } config.target_rustcflags = Some(format!( - "-L {0} -L {1} -Dwarnings -Zui-testing {2}", + "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", host_lib().join("deps").display(), cargo::TARGET_LIB.join("deps").display(), third_party_crates(), From 1cb3c00cba1d0a583280536d3d45161dc0c82308 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 25 Sep 2020 15:46:32 +0200 Subject: [PATCH 0655/1110] Use emit=link for auxiliary proc macro crates --- tests/ui/auxiliary/proc_macro_attr.rs | 1 + tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/crashes/auxiliary/proc_macro_crash.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e6626d57a77..01796d45f13 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 05ffb55f620..3df8be6c232 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index 086548e58ed..619d11cefc4 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic // ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro // crates. If we don't set this, compiletest will override the `crate_type` attribute below and From 5b484b405748fc8d7476f9a8d68d2e7227767271 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 25 Sep 2020 23:32:18 +0900 Subject: [PATCH 0656/1110] Fix the detection of build scripts --- clippy_lints/src/write.rs | 33 +++++++++---------- ...{build.rs => print_stdout_build_script.rs} | 2 ++ ...tderr => print_stdout_build_script.stderr} | 0 3 files changed, 17 insertions(+), 18 deletions(-) rename tests/ui/{build.rs => print_stdout_build_script.rs} (81%) rename tests/ui/{build.stderr => print_stdout_build_script.stderr} (100%) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 780d474ee96..0e9c7098af8 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::ops::Range; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; @@ -12,7 +11,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, FileName, Span}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -236,15 +235,19 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + fn is_build_scripts(cx: &EarlyContext<'_>) -> bool { + // We could leverage the fact that Cargo sets the crate name + // for build scripts to `build_script_build`. + cx.sess + .opts + .crate_name + .as_ref() + .map_or(false, |crate_name| crate_name == "build_script_build") + } + if mac.path == sym!(println) { - let filename = cx.sess.source_map().span_to_filename(mac.span()); - if_chain! { - if let FileName::Real(filename) = filename; - if let Some(filename) = filename.local_path().file_name(); - if filename != "build.rs"; - then { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - } + if !is_build_scripts(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { @@ -260,14 +263,8 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - if_chain! { - let filename = cx.sess.source_map().span_to_filename(mac.span()); - if let FileName::Real(filename) = filename; - if let Some(filename) = filename.local_path().file_name(); - if filename != "build.rs"; - then { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - } + if !is_build_scripts(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { diff --git a/tests/ui/build.rs b/tests/ui/print_stdout_build_script.rs similarity index 81% rename from tests/ui/build.rs rename to tests/ui/print_stdout_build_script.rs index 2d43d452a4f..b84bf9124fc 100644 --- a/tests/ui/build.rs +++ b/tests/ui/print_stdout_build_script.rs @@ -1,3 +1,5 @@ +// compile-flags: --crate-name=build_script_build + #![warn(clippy::print_stdout)] fn main() { diff --git a/tests/ui/build.stderr b/tests/ui/print_stdout_build_script.stderr similarity index 100% rename from tests/ui/build.stderr rename to tests/ui/print_stdout_build_script.stderr From 1479c18396d764482aa0e56b372c5f57a97c102b Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 15:32:03 -0500 Subject: [PATCH 0657/1110] add disallowed_method lint --- CHANGELOG.md | 1 + clippy_lints/src/disallowed_method.rs | 75 +++++++++++++++++++ clippy_lints/src/lib.rs | 6 ++ clippy_lints/src/utils/conf.rs | 2 + src/lintlist/mod.rs | 7 ++ .../toml_disallowed_method/clippy.toml | 1 + .../conf_disallowed_method.rs | 13 ++++ .../conf_disallowed_method.stderr | 16 ++++ tests/ui/disallowed_method.rs | 56 ++++++++++++++ tests/ui/disallowed_method.stderr | 22 ++++++ 10 files changed, 199 insertions(+) create mode 100644 clippy_lints/src/disallowed_method.rs create mode 100644 tests/ui-toml/toml_disallowed_method/clippy.toml create mode 100644 tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs create mode 100644 tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr create mode 100644 tests/ui/disallowed_method.rs create mode 100644 tests/ui/disallowed_method.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d1dfe36ffd8..575cbd60792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1559,6 +1559,7 @@ Released 2018-09-13 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs new file mode 100644 index 00000000000..7088b2718f2 --- /dev/null +++ b/clippy_lints/src/disallowed_method.rs @@ -0,0 +1,75 @@ +use crate::utils::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{impl_lint_pass, declare_tool_lint}; +use rustc_hir::*; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Lints for specific trait methods defined in clippy.toml + /// + /// **Why is this bad?** Some methods are undesirable in certain contexts, + /// and it would be beneficial to lint for them as needed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// foo.bad_method(); // Foo is disallowed + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// GoodStruct.bad_method(); // not disallowed + /// ``` + pub DISALLOWED_METHOD, + nursery, + "used disallowed method call" +} + +#[derive(Clone, Debug)] +pub struct DisallowedMethod { + disallowed: FxHashSet>, +} + +impl DisallowedMethod { + pub fn new(disallowed: FxHashSet) -> Self { + Self { + disallowed: disallowed.iter() + .map(|s| { + s.split("::").map(|seg| Symbol::intern(seg)).collect::>() + }) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); + +impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(path, _, _args, _) = &expr.kind { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + + let method_call = cx.get_def_path(def_id); + if self.disallowed.contains(&method_call) { + span_lint( + cx, + DISALLOWED_METHOD, + expr.span, + &format!( + "Use of a disallowed method `{}`", + method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"), + ) + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58112ac8da5..7c886ab87d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -175,6 +175,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod disallowed_method; mod doc; mod double_comparison; mod double_parens; @@ -525,6 +526,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1118,6 +1120,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(disallowed_methods.clone())); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1807,6 +1812,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c5a12ea9c8..07591ce2229 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -164,6 +164,8 @@ define_Conf! { (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), + /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses + (disallowed_methods, "disallowed_methods": Vec, ["disallowed_method::Foo::bad_method", "disallowed_method::Baz::bad_method", "disallowed_method::Quux::bad_method"].iter().map(ToString::to_string).collect()), } impl Default for Conf { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 9603023ed06..a77bbcb0abe 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "disallowed_method", + group: "nursery", + desc: "default lint description", + deprecation: None, + module: "disallowed_method", + }, Lint { name: "diverging_sub_expression", group: "complexity", diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml new file mode 100644 index 00000000000..a1f515e443d --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -0,0 +1 @@ +disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"] diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs new file mode 100644 index 00000000000..3d3f0729abd --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs @@ -0,0 +1,13 @@ +#![warn(clippy::disallowed_method)] + +extern crate regex; +use regex::Regex; + +fn main() { + let a = vec![1, 2, 3, 4]; + let re = Regex::new(r"ab.*c").unwrap(); + + re.is_match("abc"); + + a.iter().sum::(); +} diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr new file mode 100644 index 00000000000..5da551cb430 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -0,0 +1,16 @@ +error: Use of a disallowed method `regex::re_unicode::Regex::is_match` + --> $DIR/conf_disallowed_method.rs:10:5 + | +LL | re.is_match("abc"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: Use of a disallowed method `core::iter::traits::iterator::Iterator::sum` + --> $DIR/conf_disallowed_method.rs:12:5 + | +LL | a.iter().sum::(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs new file mode 100644 index 00000000000..a54a04b4d2c --- /dev/null +++ b/tests/ui/disallowed_method.rs @@ -0,0 +1,56 @@ +#![warn(clippy::disallowed_method)] +#![allow(clippy::no_effect, clippy::many_single_char_names)] + +struct ImplStruct; + +trait Baz { + fn bad_method(self); +} + +impl Baz for ImplStruct { + fn bad_method(self) {} +} + +struct Foo; + +impl Foo { + fn bad_method(self) {} +} + +struct StaticStruct; + +trait Quux { + fn bad_method(); +} + +impl Quux for StaticStruct { + fn bad_method() {} +} + +struct NormalStruct; + +impl NormalStruct { + fn bad_method(self) {} +} + +struct AttrStruct { + bad_method: i32, +} + +fn main() { + let b = ImplStruct; + let f = Foo; + let c = ImplStruct; + let n = NormalStruct; + let a = AttrStruct{ bad_method: 5 }; + + // lint these + b.bad_method(); + c.bad_method(); + f.bad_method(); + // these are good + // good because not a method call (ExprKind => Call) + StaticStruct::bad_method(); + n.bad_method(); + a.bad_method; +} diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr new file mode 100644 index 00000000000..93dabf38cfc --- /dev/null +++ b/tests/ui/disallowed_method.stderr @@ -0,0 +1,22 @@ +error: Use of a disallowed method `disallowed_method::Baz::bad_method` + --> $DIR/disallowed_method.rs:48:5 + | +LL | b.bad_method(); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: Use of a disallowed method `disallowed_method::Baz::bad_method` + --> $DIR/disallowed_method.rs:49:5 + | +LL | c.bad_method(); + | ^^^^^^^^^^^^^^ + +error: Use of a disallowed method `disallowed_method::Foo::bad_method` + --> $DIR/disallowed_method.rs:50:5 + | +LL | f.bad_method(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + From 12e5637f757f4fd4cc2331619ebeca59934a910d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 15:36:38 -0500 Subject: [PATCH 0658/1110] update unused variable --- clippy_lints/src/disallowed_method.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 7088b2718f2..5ecdcc0e08a 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -51,7 +51,7 @@ impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(path, _, _args, _) = &expr.kind { + if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); let method_call = cx.get_def_path(def_id); From f4d88cbb1f150e94c613fdd082c5bf8418443804 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 15:52:25 -0500 Subject: [PATCH 0659/1110] run cargo dev update_lints --- README.md | 2 +- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a2984d73641..62a8be0abf2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a77bbcb0abe..57dc48c0667 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -384,7 +384,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "disallowed_method", group: "nursery", - desc: "default lint description", + desc: "used disallowed method call", deprecation: None, module: "disallowed_method", }, From e1b3f85e984c9d4fba3ef5360892c88990b8391d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:00:46 -0500 Subject: [PATCH 0660/1110] run cargo dev fmt --- clippy_lints/src/disallowed_method.rs | 15 +++++++-------- tests/ui/disallowed_method.rs | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 5ecdcc0e08a..42fbff8ff87 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,9 +1,9 @@ use crate::utils::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{impl_lint_pass, declare_tool_lint}; use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; declare_clippy_lint! { @@ -38,10 +38,9 @@ pub struct DisallowedMethod { impl DisallowedMethod { pub fn new(disallowed: FxHashSet) -> Self { Self { - disallowed: disallowed.iter() - .map(|s| { - s.split("::").map(|seg| Symbol::intern(seg)).collect::>() - }) + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), } } @@ -49,7 +48,7 @@ impl DisallowedMethod { impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); -impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { +impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); @@ -67,7 +66,7 @@ impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { .map(|s| s.to_ident_string()) .collect::>() .join("::"), - ) + ), ); } } diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs index a54a04b4d2c..46c9185268c 100644 --- a/tests/ui/disallowed_method.rs +++ b/tests/ui/disallowed_method.rs @@ -42,7 +42,7 @@ fn main() { let f = Foo; let c = ImplStruct; let n = NormalStruct; - let a = AttrStruct{ bad_method: 5 }; + let a = AttrStruct { bad_method: 5 }; // lint these b.bad_method(); From 9eb52d2eb6c3812367276bc19c23f5d7368de664 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:15:24 -0500 Subject: [PATCH 0661/1110] update toml_unknown_key test --- tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6fbba01416a..103ec27e7d7 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `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-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error From 725a0ef8b1948e459d266cd0ab33dda0c8bc3708 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:26:29 -0500 Subject: [PATCH 0662/1110] change config variables to reference, remove wildcard import --- clippy_lints/src/disallowed_method.rs | 4 ++-- clippy_lints/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 42fbff8ff87..7638019340f 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,7 +1,7 @@ use crate::utils::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; @@ -36,7 +36,7 @@ pub struct DisallowedMethod { } impl DisallowedMethod { - pub fn new(disallowed: FxHashSet) -> Self { + pub fn new(disallowed: &FxHashSet) -> Self { Self { disallowed: disallowed .iter() diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7c886ab87d0..7b6efc660af 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1121,7 +1121,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); - store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(disallowed_methods.clone())); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ From 3886edb05a2b570664f7249620766582e55a74aa Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:43:29 -0500 Subject: [PATCH 0663/1110] fix error message --- clippy_lints/src/disallowed_method.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 7638019340f..eea369924a6 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -16,14 +16,14 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust + /// ```rust,ignore /// // example code where clippy issues a warning /// foo.bad_method(); // Foo is disallowed /// ``` /// Use instead: - /// ```rust + /// ```rust,ignore /// // example code which does not raise clippy warning - /// GoodStruct.bad_method(); // not disallowed + /// goodStruct.bad_method(); // not disallowed /// ``` pub DISALLOWED_METHOD, nursery, From f9da2946d81a973b3c25aa5f4739ac7e05c27278 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 09:38:19 -0500 Subject: [PATCH 0664/1110] update error message, refactor disallowed_method --- clippy_lints/src/disallowed_method.rs | 17 ++++++++--------- .../conf_disallowed_method.stderr | 4 ++-- tests/ui/disallowed_method.stderr | 6 +++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index eea369924a6..603f776e688 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// ``` pub DISALLOWED_METHOD, nursery, - "used disallowed method call" + "use of a disallowed method call" } #[derive(Clone, Debug)] @@ -55,18 +55,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { let method_call = cx.get_def_path(def_id); if self.disallowed.contains(&method_call) { + let method = method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"); + span_lint( cx, DISALLOWED_METHOD, expr.span, - &format!( - "Use of a disallowed method `{}`", - method_call - .iter() - .map(|s| s.to_ident_string()) - .collect::>() - .join("::"), - ), + &format!("use of a disallowed method `{}`", method), ); } } diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr index 5da551cb430..ed91b5a6796 100644 --- a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -1,4 +1,4 @@ -error: Use of a disallowed method `regex::re_unicode::Regex::is_match` +error: use of a disallowed method `regex::re_unicode::Regex::is_match` --> $DIR/conf_disallowed_method.rs:10:5 | LL | re.is_match("abc"); @@ -6,7 +6,7 @@ LL | re.is_match("abc"); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: Use of a disallowed method `core::iter::traits::iterator::Iterator::sum` +error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum` --> $DIR/conf_disallowed_method.rs:12:5 | LL | a.iter().sum::(); diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr index 93dabf38cfc..40db1b946d8 100644 --- a/tests/ui/disallowed_method.stderr +++ b/tests/ui/disallowed_method.stderr @@ -1,4 +1,4 @@ -error: Use of a disallowed method `disallowed_method::Baz::bad_method` +error: use of a disallowed method `disallowed_method::Baz::bad_method` --> $DIR/disallowed_method.rs:48:5 | LL | b.bad_method(); @@ -6,13 +6,13 @@ LL | b.bad_method(); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: Use of a disallowed method `disallowed_method::Baz::bad_method` +error: use of a disallowed method `disallowed_method::Baz::bad_method` --> $DIR/disallowed_method.rs:49:5 | LL | c.bad_method(); | ^^^^^^^^^^^^^^ -error: Use of a disallowed method `disallowed_method::Foo::bad_method` +error: use of a disallowed method `disallowed_method::Foo::bad_method` --> $DIR/disallowed_method.rs:50:5 | LL | f.bad_method(); From d18653158ddde023f8165ef7ba3c607661398abf Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 11:03:45 -0500 Subject: [PATCH 0665/1110] remove useless test, update disallowed_method description --- clippy_lints/src/disallowed_method.rs | 4 +- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/disallowed_method.rs | 56 --------------------------- tests/ui/disallowed_method.stderr | 22 ----------- 4 files changed, 3 insertions(+), 81 deletions(-) delete mode 100644 tests/ui/disallowed_method.rs delete mode 100644 tests/ui/disallowed_method.stderr diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 603f776e688..581c3242e37 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -18,12 +18,12 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // example code where clippy issues a warning - /// foo.bad_method(); // Foo is disallowed + /// foo.bad_method(); // Foo::bad_method is disallowed in the configuration /// ``` /// Use instead: /// ```rust,ignore /// // example code which does not raise clippy warning - /// goodStruct.bad_method(); // not disallowed + /// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed /// ``` pub DISALLOWED_METHOD, nursery, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 07591ce2229..03f8c5a2c07 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -165,7 +165,7 @@ define_Conf! { /// 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), /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses - (disallowed_methods, "disallowed_methods": Vec, ["disallowed_method::Foo::bad_method", "disallowed_method::Baz::bad_method", "disallowed_method::Quux::bad_method"].iter().map(ToString::to_string).collect()), + (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), } impl Default for Conf { diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs deleted file mode 100644 index 46c9185268c..00000000000 --- a/tests/ui/disallowed_method.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![warn(clippy::disallowed_method)] -#![allow(clippy::no_effect, clippy::many_single_char_names)] - -struct ImplStruct; - -trait Baz { - fn bad_method(self); -} - -impl Baz for ImplStruct { - fn bad_method(self) {} -} - -struct Foo; - -impl Foo { - fn bad_method(self) {} -} - -struct StaticStruct; - -trait Quux { - fn bad_method(); -} - -impl Quux for StaticStruct { - fn bad_method() {} -} - -struct NormalStruct; - -impl NormalStruct { - fn bad_method(self) {} -} - -struct AttrStruct { - bad_method: i32, -} - -fn main() { - let b = ImplStruct; - let f = Foo; - let c = ImplStruct; - let n = NormalStruct; - let a = AttrStruct { bad_method: 5 }; - - // lint these - b.bad_method(); - c.bad_method(); - f.bad_method(); - // these are good - // good because not a method call (ExprKind => Call) - StaticStruct::bad_method(); - n.bad_method(); - a.bad_method; -} diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr deleted file mode 100644 index 40db1b946d8..00000000000 --- a/tests/ui/disallowed_method.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: use of a disallowed method `disallowed_method::Baz::bad_method` - --> $DIR/disallowed_method.rs:48:5 - | -LL | b.bad_method(); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-method` implied by `-D warnings` - -error: use of a disallowed method `disallowed_method::Baz::bad_method` - --> $DIR/disallowed_method.rs:49:5 - | -LL | c.bad_method(); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `disallowed_method::Foo::bad_method` - --> $DIR/disallowed_method.rs:50:5 - | -LL | f.bad_method(); - | ^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - From 5f7b6437587bb0eda69c754ff52f92ed6eba2d72 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 11:12:45 -0500 Subject: [PATCH 0666/1110] update lint description --- src/lintlist/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 57dc48c0667..76e655ad603 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -384,7 +384,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "disallowed_method", group: "nursery", - desc: "used disallowed method call", + desc: "use of a disallowed method call", deprecation: None, module: "disallowed_method", }, From 83294f894d477def457d54f2391a287bcb949f06 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 26 Sep 2020 23:10:25 +0900 Subject: [PATCH 0667/1110] Some small fixes --- clippy_lints/src/write.rs | 9 ++++----- tests/ui/print_stdout_build_script.rs | 2 +- tests/ui/print_stdout_build_script.stderr | 0 3 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 tests/ui/print_stdout_build_script.stderr diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0e9c7098af8..d9d60fffcd7 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -235,9 +235,8 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { - fn is_build_scripts(cx: &EarlyContext<'_>) -> bool { - // We could leverage the fact that Cargo sets the crate name - // for build scripts to `build_script_build`. + fn is_build_script(cx: &EarlyContext<'_>) -> bool { + // Cargo sets the crate name for build scripts to `build_script_build` cx.sess .opts .crate_name @@ -246,7 +245,7 @@ impl EarlyLintPass for Write { } if mac.path == sym!(println) { - if !is_build_scripts(cx) { + if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { @@ -263,7 +262,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - if !is_build_scripts(cx) { + if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs index b84bf9124fc..997ebef8a69 100644 --- a/tests/ui/print_stdout_build_script.rs +++ b/tests/ui/print_stdout_build_script.rs @@ -5,7 +5,7 @@ fn main() { // Fix #6041 // - // The `print_stdout` shouldn't be linted in `build.rs` + // The `print_stdout` lint shouldn't emit in `build.rs` // as these methods are used for the build script. println!("Hello"); print!("Hello"); diff --git a/tests/ui/print_stdout_build_script.stderr b/tests/ui/print_stdout_build_script.stderr deleted file mode 100644 index e69de29bb2d..00000000000 From 71b6d54cd911bdbbc8564dfb17e991cfa1e9a9a8 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 26 Sep 2020 23:54:18 +0900 Subject: [PATCH 0668/1110] Add build script but does not work in the dogfood test --- clippy_workspace_tests/build.rs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 clippy_workspace_tests/build.rs diff --git a/clippy_workspace_tests/build.rs b/clippy_workspace_tests/build.rs new file mode 100644 index 00000000000..3cc95765210 --- /dev/null +++ b/clippy_workspace_tests/build.rs @@ -0,0 +1,5 @@ +fn main() { + // Test for #6041 + println!("Hello"); + print!("Hello"); +} From 1214a858e0804aa1707e5b87a8eeefa4d207e8c8 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 26 Sep 2020 22:22:47 +0200 Subject: [PATCH 0669/1110] Add missing attr to clippy_workspace_tests/build.rs --- clippy_workspace_tests/build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_workspace_tests/build.rs b/clippy_workspace_tests/build.rs index 3cc95765210..3507168a3a9 100644 --- a/clippy_workspace_tests/build.rs +++ b/clippy_workspace_tests/build.rs @@ -1,3 +1,5 @@ +#![deny(clippy::print_stdout)] + fn main() { // Test for #6041 println!("Hello"); From 00e641b91417a0b4489544406f60a1e2babe7c88 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Sat, 26 Sep 2020 17:19:12 -0700 Subject: [PATCH 0670/1110] lints: clarify rc_buffer and add caveats This didn't display some types properly in the docs due the lack of code formatting. Also, refs for the caveat: https://github.com/rust-lang/rust-clippy/pull/6044#issuecomment-699559082 https://github.com/http-rs/surf/pull/242 --- clippy_lints/src/types.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a29a199b8c3..17d950169fd 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -216,18 +216,19 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. /// - /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since - /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since + /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. /// - /// While mutating a buffer type would still be possible with Rc::get_mut(), it only - /// works if there are no additional references yet, which defeats the purpose of + /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only + /// works if there are no additional references yet, which usually defeats the purpose of /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner - /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally /// be used. /// - /// **Known problems:** None. + /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for + /// cases where mutation only happens before there are any additional references. /// /// **Example:** /// ```rust,ignore From 4918e7ad62259e4c35a0b9d6ac0f0e98e51ff7b1 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 14:19:43 +1300 Subject: [PATCH 0671/1110] Replace `snippet_opt` + `unwrap_or_else` with `snippet` --- clippy_lints/src/loops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4ff567ffb0e..647537933f7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,9 +5,8 @@ use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - SpanlessEq, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -1121,8 +1120,8 @@ fn build_manual_memcpy_suggestion<'tcx>( let (dst_offset, dst_limit) = print_offset_and_limit(&dst); let (src_offset, src_limit) = print_offset_and_limit(&src); - let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); - let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); + let dst_base_str = snippet(cx, dst.base.span, "???"); + let src_base_str = snippet(cx, src.base.span, "???"); let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { dst_base_str @@ -1133,6 +1132,7 @@ fn build_manual_memcpy_suggestion<'tcx>( dst_offset.maybe_par(), dst_limit.maybe_par() ) + .into() }; format!( From 5c71352b18ee7a48e825aefd2862b8e0d16ea45b Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 14:52:06 +1300 Subject: [PATCH 0672/1110] Prevent unnecessary lints from triggering --- clippy_lints/src/loops.rs | 12 ++++++++---- tests/ui/manual_memcpy.rs | 1 - tests/ui/manual_memcpy.stderr | 20 ++++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 647537933f7..f2df53aee4f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -768,12 +768,14 @@ fn check_for_loop<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - check_for_loop_range(cx, pat, arg, body, expr); + let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); + if !is_manual_memcpy_triggered { + check_for_loop_range(cx, pat, arg, body, expr); + check_for_loop_explicit_counter(cx, pat, arg, body, expr); + } check_for_loop_arg(cx, pat, arg, expr); - check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); - detect_manual_memcpy(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1152,7 +1154,7 @@ fn detect_manual_memcpy<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, -) { +) -> bool { if let Some(higher::Range { start: Some(start), end: Some(end), @@ -1222,9 +1224,11 @@ fn detect_manual_memcpy<'tcx>( big_sugg, Applicability::Unspecified, ); + return true; } } } + false } // Scans the body of the for loop and determines whether lint should be given diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 8318fd89811..84758275dd7 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,7 +115,6 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } -#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { let mut count = 0; for i in 3..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index d189bde0508..464b18984fb 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -79,49 +79,49 @@ 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:121:14 + --> $DIR/manual_memcpy.rs:120:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:127:14 + --> $DIR/manual_memcpy.rs:126:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:133:14 + --> $DIR/manual_memcpy.rs:132:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:139:14 + --> $DIR/manual_memcpy.rs:138:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:145:14 + --> $DIR/manual_memcpy.rs:144:14 | LL | for i in 3..(3 + src.len()) { | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:151:14 + --> $DIR/manual_memcpy.rs:150:14 | LL | for i in 5..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:157:14 + --> $DIR/manual_memcpy.rs:156:14 | LL | for i in 3..10 { | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:164:14 + --> $DIR/manual_memcpy.rs:163:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:174:14 + --> $DIR/manual_memcpy.rs:173:14 | LL | for i in 0..1 << 1 { | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:182:14 + --> $DIR/manual_memcpy.rs:181:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` From 99aceebf1c7cb382e18d66914bd9f576e529aa99 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 16:38:41 +1300 Subject: [PATCH 0673/1110] Use the spans of the entire `for` loops for suggestions --- clippy_lints/src/loops.rs | 23 ++-- tests/ui/manual_memcpy.stderr | 196 ++++++++++++++++++++++------------ 2 files changed, 140 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f2df53aee4f..7c8b6f483bc 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -779,6 +779,17 @@ fn check_for_loop<'tcx>( detect_same_item_push(cx, pat, arg, body, expr); } +// this function assumes the given expression is a `for` loop. +fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { + // for some reason this is the only way to get the `Span` + // of the entire `for` loop + if let ExprKind::Match(_, arms, _) = &expr.kind { + arms[0].body.span + } else { + unreachable!() + } +} + fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if_chain! { if let ExprKind::Path(qpath) = &expr.kind; @@ -1138,7 +1149,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.clone_from_slice(&{}[{}..{}])", + "{}.clone_from_slice(&{}[{}..{}]);", dst, src_base_str, src_offset.maybe_par(), @@ -1218,7 +1229,7 @@ fn detect_manual_memcpy<'tcx>( span_lint_and_sugg( cx, MANUAL_MEMCPY, - expr.span, + get_span_of_entire_for_loop(expr), "it looks like you're manually copying between slices", "try replacing the loop by", big_sugg, @@ -1734,13 +1745,7 @@ fn check_for_loop_explicit_counter<'tcx>( then { let mut applicability = Applicability::MachineApplicable; - // for some reason this is the only way to get the `Span` - // of the entire `for` loop - let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind { - arms[0].body.span - } else { - unreachable!() - }; + let for_span = get_span_of_entire_for_loop(expr); span_lint_and_sugg( cx, diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 464b18984fb..db62ed90d97 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -1,148 +1,204 @@ error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:14 + --> $DIR/manual_memcpy.rs:7:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:14 + --> $DIR/manual_memcpy.rs:12:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i + 10] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:14 + --> $DIR/manual_memcpy.rs:17:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i + 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:14 + --> $DIR/manual_memcpy.rs:22:5 | -LL | for i in 11..src.len() { - | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])` +LL | / for i in 11..src.len() { +LL | | dst[i] = src[i - 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:14 + --> $DIR/manual_memcpy.rs:27:5 | -LL | for i in 0..dst.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])` +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:14 + --> $DIR/manual_memcpy.rs:40:5 | -LL | for i in 10..256 { - | ^^^^^^^ +LL | / for i in 10..256 { +LL | | dst[i] = src[i - 5]; +LL | | dst2[i + 500] = src[i] +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]) -LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) { +LL | dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:14 + --> $DIR/manual_memcpy.rs:52:5 | -LL | for i in 10..LOOP_OFFSET { - | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])` +LL | / for i in 10..LOOP_OFFSET { +LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:14 + --> $DIR/manual_memcpy.rs:65:5 | -LL | for i in 0..src_vec.len() { - | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])` +LL | / for i in 0..src_vec.len() { +LL | | dst_vec[i] = src_vec[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:14 + --> $DIR/manual_memcpy.rs:94:5 | -LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)])` +LL | / for i in from..from + src.len() { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ 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 + --> $DIR/manual_memcpy.rs:98:5 | -LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)])` +LL | / for i in from..from + 3 { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ 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:103:14 + --> $DIR/manual_memcpy.rs:103:5 | -LL | for i in 0..5 { - | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])` +LL | / for i in 0..5 { +LL | | dst[i - 0] = src[i]; +LL | | } + | |_____^ 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 + --> $DIR/manual_memcpy.rs:108:5 | -LL | for i in 0..0 { - | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` +LL | / for i in 0..0 { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ 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 + --> $DIR/manual_memcpy.rs:120:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:14 + --> $DIR/manual_memcpy.rs:126:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:14 + --> $DIR/manual_memcpy.rs:132:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:14 + --> $DIR/manual_memcpy.rs:138:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:14 + --> $DIR/manual_memcpy.rs:144:5 | -LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:14 + --> $DIR/manual_memcpy.rs:150:5 | -LL | for i in 5..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:14 + --> $DIR/manual_memcpy.rs:156:5 | -LL | for i in 3..10 { - | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:14 + --> $DIR/manual_memcpy.rs:163:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:14 + --> $DIR/manual_memcpy.rs:173:5 | -LL | for i in 0..1 << 1 { - | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:14 + --> $DIR/manual_memcpy.rs:181:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i].clone(); +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` error: aborting due to 22 previous errors From cd4706413fb891ffe33ef06e0c229d97258fbfaf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 27 Sep 2020 15:17:13 +0200 Subject: [PATCH 0674/1110] Run cargo dev fmt --- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/utils/mod.rs | 2 +- .../src/utils/qualify_min_const_fn.rs | 183 +++++++----------- 3 files changed, 71 insertions(+), 116 deletions(-) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index e5f7cc51111..3e786da28df 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,10 +1,10 @@ +use crate::utils::qualify_min_const_fn::is_min_const_fn; use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use crate::utils::qualify_min_const_fn::is_min_const_fn; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index f59ed6df1a9..dfe2aadffc0 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -18,9 +18,9 @@ pub mod internal_lints; pub mod numeric_literal; pub mod paths; pub mod ptr; +pub mod qualify_min_const_fn; pub mod sugg; pub mod usage; -pub mod qualify_min_const_fn; pub use self::attrs::*; pub use self::diagnostics::*; diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index 6809b1fa88d..c1684575930 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::*; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; -use rustc_span::symbol::{sym}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; use std::borrow::Cow; @@ -23,15 +23,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, - ty::PredicateAtom::ObjectSafe(_) => { - panic!("object safe predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::ClosureKind(..) => { - panic!("closure kind predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::Subtype(_) => { - panic!("subtype predicate on function: {:#?}", predicate) - } + ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), + ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), + ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), ty::PredicateAtom::Trait(pred, _) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; @@ -47,12 +41,12 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - on const fn parameters are unstable" .into(), )); - } + }, // other kinds of bounds are either tautologies // or cause errors in other passes _ => continue, } - } + }, } } match predicates.parent { @@ -92,24 +86,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { - return Err((span, "mutable references in const fn are unstable".into())); - } + return Err((span, "mutable references in const fn are unstable".into())); + }, ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { - return Err((span, "function pointers in const fn are unstable".into())); - } + return Err((span, "function pointers in const fn are unstable".into())); + }, ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { - ty::ExistentialPredicate::AutoTrait(_) - | ty::ExistentialPredicate::Projection(_) => { + ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { return Err(( span, "trait bounds other than `Sized` \ on const fn parameters are unstable" .into(), )); - } + }, ty::ExistentialPredicate::Trait(trait_ref) => { if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { return Err(( @@ -119,34 +112,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { .into(), )); } - } + }, } } - } - _ => {} + }, + _ => {}, } } Ok(()) } -fn check_rvalue( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - rvalue: &Rvalue<'tcx>, - span: Span, -) -> McfResult { +fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult { match rvalue { - Rvalue::ThreadLocalRef(_) => { - Err((span, "cannot access thread local storage in const fn".into())) - } - Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { - check_operand(tcx, operand, span, body) - } - Rvalue::Len(place) - | Rvalue::Discriminant(place) - | Rvalue::Ref(_, _, place) - | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body), + Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), + Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + check_place(tcx, *place, span, body) + }, Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { use rustc_middle::ty::cast::CastTy; let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); @@ -154,20 +136,16 @@ fn check_rvalue( match (cast_in, cast_out) { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { Err((span, "casting pointers to ints is unstable in const fn".into())) - } + }, _ => check_operand(tcx, operand, span, body), } - } - Rvalue::Cast( - CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), - operand, - _, - ) => check_operand(tcx, operand, span, body), + }, + Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { + check_operand(tcx, operand, span, body) + }, Rvalue::Cast( CastKind::Pointer( - PointerCast::UnsafeFnPointer - | PointerCast::ClosureFnPointer(_) - | PointerCast::ReifyFnPointer, + PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, ), _, _, @@ -177,10 +155,7 @@ fn check_rvalue( deref_ty.ty } else { // We cannot allow this for now. - return Err(( - span, - "unsizing casts are only allowed for references right now".into(), - )); + return Err((span, "unsizing casts are only allowed for references right now".into())); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { @@ -191,7 +166,7 @@ fn check_rvalue( // We just can't allow trait objects until we have figured out trait method calls. Err((span, "unsizing casts are not allowed in const fn".into())) } - } + }, // binops are fine on integers Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { check_operand(tcx, lhs, span, body)?; @@ -200,13 +175,14 @@ fn check_rvalue( if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) } else { - Err((span, "only int, `bool` and `char` operations are stable in const fn".into())) + Err(( + span, + "only int, `bool` and `char` operations are stable in const fn".into(), + )) } - } + }, Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()), - Rvalue::NullaryOp(NullOp::Box, _) => { - Err((span, "heap allocations are not allowed in const fn".into())) - } + Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { @@ -214,39 +190,30 @@ fn check_rvalue( } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } - } + }, Rvalue::Aggregate(_, operands) => { for operand in operands { check_operand(tcx, operand, span, body)?; } Ok(()) - } + }, } } -fn check_statement( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - statement: &Statement<'tcx>, -) -> McfResult { +fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_rvalue(tcx, body, def_id, rval, span) - } + }, StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } => { - check_place(tcx, **place, span, body) - } + StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), - StatementKind::LlvmInlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), // These are all NOPs StatementKind::StorageLive(_) @@ -258,12 +225,7 @@ fn check_statement( } } -fn check_operand( - tcx: TyCtxt<'tcx>, - operand: &Operand<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { match operand { Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { @@ -273,12 +235,7 @@ fn check_operand( } } -fn check_place( - tcx: TyCtxt<'tcx>, - place: Place<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); while let &[ref proj_base @ .., elem] = cursor { cursor = proj_base; @@ -288,26 +245,22 @@ fn check_place( if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); + return Err((span, "accessing union fields is unstable".into())); } } - } + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref - | ProjectionElem::Index(_) => {} + | ProjectionElem::Index(_) => {}, } } Ok(()) } -fn check_terminator( - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - terminator: &Terminator<'tcx>, -) -> McfResult { +fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -317,20 +270,23 @@ fn check_terminator( | TerminatorKind::Resume | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), TerminatorKind::DropAndReplace { place, value, .. } => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_operand(tcx, value, span, body) - } + }, - TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => { - check_operand(tcx, discr, span, body) - } + TerminatorKind::SwitchInt { + discr, + switch_ty: _, + values: _, + targets: _, + } => check_operand(tcx, discr, span, body), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn generators are unstable".into())) - } + }, TerminatorKind::Call { func, @@ -342,8 +298,7 @@ fn check_terminator( } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) - { + if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { return Err(( span, format!( @@ -359,9 +314,7 @@ fn check_terminator( // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic - && tcx.item_name(fn_def_id) == sym::transmute - { + if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute { return Err(( span, "can only call `transmute` from const items, not `const fn`".into(), @@ -377,14 +330,16 @@ fn check_terminator( } else { Err((span, "can only call other const fns within const fn".into())) } - } + }, - TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { - check_operand(tcx, cond, span, body) - } + TerminatorKind::Assert { + cond, + expected: _, + msg: _, + target: _, + cleanup: _, + } => check_operand(tcx, cond, span, body), - TerminatorKind::InlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } From 8bf27c5e92af39215a3d1da992a7207dafc883e1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 27 Sep 2020 15:20:19 +0200 Subject: [PATCH 0675/1110] Fix dogfood --- clippy_lints/src/utils/qualify_min_const_fn.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index c1684575930..3773b9d9a2e 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -1,6 +1,9 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::mir::*; +use rustc_middle::mir::{ + Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -208,8 +211,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen check_rvalue(tcx, body, def_id, rval, span) }, - StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), - + StatementKind::FakeRead(_, place) | // just an assignment StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), @@ -237,7 +239,7 @@ fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: & fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { + while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; match elem { ProjectionElem::Field(..) => { From 9725f00f4dd089b875a5d5306ff906494e041f38 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 28 Sep 2020 02:27:55 +1300 Subject: [PATCH 0676/1110] Use the `From` trait to make `MinifyingSugg` --- clippy_lints/src/loops.rs | 84 +++++++++++++++------------------- clippy_lints/src/utils/sugg.rs | 6 ++- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7c8b6f483bc..215700fed8c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -817,22 +817,22 @@ struct Offset { } impl Offset { - fn negative(value: MinifyingSugg<'static>) -> Self { + fn negative(value: Sugg<'static>) -> Self { Self { - value, + value: value.into(), sign: OffsetSign::Negative, } } - fn positive(value: MinifyingSugg<'static>) -> Self { + fn positive(value: Sugg<'static>) -> Self { Self { - value, + value: value.into(), sign: OffsetSign::Positive, } } fn empty() -> Self { - Self::positive(MinifyingSugg::non_paren("0")) + Self::positive(sugg::ZERO) } } @@ -844,30 +844,22 @@ fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'st } #[derive(Clone)] -struct MinifyingSugg<'a>(sugg::Sugg<'a>); - -impl std::fmt::Display for MinifyingSugg<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - std::fmt::Display::fmt(&self.0, f) - } -} +struct MinifyingSugg<'a>(Sugg<'a>); impl<'a> MinifyingSugg<'a> { fn as_str(&self) -> &str { - let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0; + let Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) = &self.0; s.as_ref() } - fn hir(cx: &LateContext<'_>, expr: &Expr<'_>, default: &'a str) -> Self { - Self(sugg::Sugg::hir(cx, expr, default)) + fn into_sugg(self) -> Sugg<'a> { + self.0 } +} - fn maybe_par(self) -> Self { - Self(self.0.maybe_par()) - } - - fn non_paren(str: impl Into>) -> Self { - Self(sugg::Sugg::NonParen(str.into())) +impl<'a> From> for MinifyingSugg<'a> { + fn from(sugg: Sugg<'a>) -> Self { + Self(sugg) } } @@ -877,7 +869,7 @@ impl std::ops::Add for &MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { ("0", _) => rhs.clone(), (_, "0") => self.clone(), - (_, _) => MinifyingSugg(&self.0 + &rhs.0), + (_, _) => (&self.0 + &rhs.0).into(), } } } @@ -887,9 +879,9 @@ impl std::ops::Sub for &MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self.clone(), - ("0", _) => MinifyingSugg(-(rhs.0.clone())), - (x, y) if x == y => MinifyingSugg::non_paren("0"), - (_, _) => MinifyingSugg(&self.0 - &rhs.0), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (&self.0 - &rhs.0).into(), } } } @@ -900,7 +892,7 @@ impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { ("0", _) => rhs.clone(), (_, "0") => self, - (_, _) => MinifyingSugg(self.0 + &rhs.0), + (_, _) => (self.0 + &rhs.0).into(), } } } @@ -910,9 +902,9 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self, - ("0", _) => MinifyingSugg(-(rhs.0.clone())), - (x, y) if x == y => MinifyingSugg::non_paren("0"), - (_, _) => MinifyingSugg(self.0 - &rhs.0), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (self.0 - &rhs.0).into(), } } } @@ -969,19 +961,15 @@ fn get_details_from_idx<'tcx>( }) } - fn get_offset<'tcx>( - cx: &LateContext<'tcx>, - e: &Expr<'_>, - starts: &[Start<'tcx>], - ) -> Option> { + fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { match &e.kind { ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), + ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), _ => None, }, ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { // `e` is always non paren as it's a `Path` - Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) + Some(Sugg::NonParen(snippet(cx, e.span, "???"))) }, _ => None, } @@ -1072,7 +1060,7 @@ fn build_manual_memcpy_suggestion<'tcx>( ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { - MinifyingSugg::non_paren("") + sugg::EMPTY.into() } else { offset } @@ -1088,14 +1076,14 @@ fn build_manual_memcpy_suggestion<'tcx>( if var_def_id(cx, arg) == var_def_id(cx, base); then { if sugg.as_str() == end_str { - MinifyingSugg::non_paren("") + sugg::EMPTY.into() } else { sugg } } else { match limits { ast::RangeLimits::Closed => { - sugg + &MinifyingSugg::non_paren("1") + sugg + &sugg::ONE.into() }, ast::RangeLimits::HalfOpen => sugg, } @@ -1103,29 +1091,31 @@ fn build_manual_memcpy_suggestion<'tcx>( } }; - let start_str = MinifyingSugg::hir(cx, start, ""); - let end_str = MinifyingSugg::hir(cx, end, ""); + let start_str = Sugg::hir(cx, start, "").into(); + let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { StartKind::Range => ( - print_offset(apply_offset(&start_str, &idx_expr.idx_offset)), + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(), print_limit( end, end_str.as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset), - ), + ) + .into_sugg(), ), StartKind::Counter { initializer } => { - let counter_start = MinifyingSugg::hir(cx, initializer, ""); + let counter_start = Sugg::hir(cx, initializer, "").into(); ( - print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)), + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(), print_limit( end, end_str.as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, - ), + ) + .into_sugg(), ) }, }; @@ -1136,7 +1126,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst_base_str = snippet(cx, dst.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???"); - let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { + let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { dst_base_str } else { format!( diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 062b273c0f4..0b2cb667bf4 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -16,7 +16,7 @@ use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Sugg<'a> { /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -27,8 +27,12 @@ pub enum Sugg<'a> { BinOp(AssocOp, Cow<'a, str>), } +/// Literal constant `0`, for convenience. +pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0")); /// Literal constant `1`, for convenience. pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1")); +/// a constant represents an empty string, for convenience. +pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { From ec94bd6cb45e337fd10ca19fadb9a71ecb67db5f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 28 Sep 2020 02:43:45 +1300 Subject: [PATCH 0677/1110] split up the `manual_memcpy` test --- tests/ui/manual_memcpy/with_loop_counters.rs | 64 ++++++++++ .../manual_memcpy/with_loop_counters.stderr | 93 ++++++++++++++ .../without_loop_counters.rs} | 61 --------- .../without_loop_counters.stderr} | 117 +++--------------- 4 files changed, 171 insertions(+), 164 deletions(-) create mode 100644 tests/ui/manual_memcpy/with_loop_counters.rs create mode 100644 tests/ui/manual_memcpy/with_loop_counters.stderr rename tests/ui/{manual_memcpy.rs => manual_memcpy/without_loop_counters.rs} (68%) rename tests/ui/{manual_memcpy.stderr => manual_memcpy/without_loop_counters.stderr} (50%) diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs new file mode 100644 index 00000000000..a49ba9eb10a --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -0,0 +1,64 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] + +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..(3 + src.len()) { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + for i in 5..src.len() { + dst[i] = src[count - 2]; + count += 1; + } + + let mut count = 5; + for i in 3..10 { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + let mut count2 = 30; + for i in 0..src.len() { + dst[count] = src[i]; + dst2[count2] = src[i]; + count += 1; + count2 += 1; + } + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetric ones + let mut count = 0 << 1; + for i in 0..1 << 1 { + dst[count] = src[i + 2]; + count += 1; + } +} + +fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr new file mode 100644 index 00000000000..24393ad9b4d --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -0,0 +1,93 @@ +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:5:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | + = note: `-D clippy::manual-memcpy` implied by `-D warnings` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:11:5 + | +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:17:5 + | +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:23:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:29:5 + | +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:35:5 + | +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:41:5 + | +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:48:5 + | +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ + | +help: try replacing the loop by + | +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); + | + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:58:5 + | +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` + +error: aborting due to 9 previous errors + diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy/without_loop_counters.rs similarity index 68% rename from tests/ui/manual_memcpy.rs rename to tests/ui/manual_memcpy/without_loop_counters.rs index 84758275dd7..0083f94798f 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -115,67 +115,6 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } -pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { - let mut count = 0; - for i in 3..src.len() { - dst[i] = src[count]; - count += 1; - } - - let mut count = 0; - for i in 3..src.len() { - dst[count] = src[i]; - count += 1; - } - - let mut count = 3; - for i in 0..src.len() { - dst[count] = src[i]; - count += 1; - } - - let mut count = 3; - for i in 0..src.len() { - dst[i] = src[count]; - count += 1; - } - - let mut count = 0; - for i in 3..(3 + src.len()) { - dst[i] = src[count]; - count += 1; - } - - let mut count = 3; - for i in 5..src.len() { - dst[i] = src[count - 2]; - count += 1; - } - - let mut count = 5; - for i in 3..10 { - dst[i] = src[count]; - count += 1; - } - - let mut count = 3; - let mut count2 = 30; - for i in 0..src.len() { - dst[count] = src[i]; - dst2[count2] = src[i]; - count += 1; - count2 += 1; - } - - // make sure parentheses are added properly to bitwise operators, which have lower precedence than - // arithmetric ones - let mut count = 0 << 1; - for i in 0..1 << 1 { - dst[count] = src[i + 2]; - count += 1; - } -} - #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] pub fn manual_clone(src: &[String], dst: &mut [String]) { for i in 0..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr similarity index 50% rename from tests/ui/manual_memcpy.stderr rename to tests/ui/manual_memcpy/without_loop_counters.stderr index db62ed90d97..54b966f6e54 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:5 + --> $DIR/without_loop_counters.rs:7:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i]; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::manual-memcpy` implied by `-D warnings` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:5 + --> $DIR/without_loop_counters.rs:12:5 | LL | / for i in 0..src.len() { LL | | dst[i + 10] = src[i]; @@ -17,7 +17,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:5 + --> $DIR/without_loop_counters.rs:17:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i + 10]; @@ -25,7 +25,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:5 + --> $DIR/without_loop_counters.rs:22:5 | LL | / for i in 11..src.len() { LL | | dst[i] = src[i - 10]; @@ -33,7 +33,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:5 + --> $DIR/without_loop_counters.rs:27:5 | LL | / for i in 0..dst.len() { LL | | dst[i] = src[i]; @@ -41,7 +41,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:5 + --> $DIR/without_loop_counters.rs:40:5 | LL | / for i in 10..256 { LL | | dst[i] = src[i - 5]; @@ -56,7 +56,7 @@ LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:5 + --> $DIR/without_loop_counters.rs:52:5 | LL | / for i in 10..LOOP_OFFSET { LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; @@ -64,7 +64,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:5 + --> $DIR/without_loop_counters.rs:65:5 | LL | / for i in 0..src_vec.len() { LL | | dst_vec[i] = src_vec[i]; @@ -72,7 +72,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:5 + --> $DIR/without_loop_counters.rs:94:5 | LL | / for i in from..from + src.len() { LL | | dst[i] = src[i - from]; @@ -80,7 +80,7 @@ LL | | } | |_____^ 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:5 + --> $DIR/without_loop_counters.rs:98:5 | LL | / for i in from..from + 3 { LL | | dst[i] = src[i - from]; @@ -88,7 +88,7 @@ LL | | } | |_____^ 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:103:5 + --> $DIR/without_loop_counters.rs:103:5 | LL | / for i in 0..5 { LL | | dst[i - 0] = src[i]; @@ -96,7 +96,7 @@ LL | | } | |_____^ 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:5 + --> $DIR/without_loop_counters.rs:108:5 | LL | / for i in 0..0 { LL | | dst[i] = src[i]; @@ -104,101 +104,12 @@ LL | | } | |_____^ 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:5 - | -LL | / for i in 3..src.len() { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:5 - | -LL | / for i in 3..src.len() { -LL | | dst[count] = src[i]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:5 - | -LL | / for i in 0..src.len() { -LL | | dst[count] = src[i]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:5 - | -LL | / for i in 3..(3 + src.len()) { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:5 - | -LL | / for i in 5..src.len() { -LL | | dst[i] = src[count - 2]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:5 - | -LL | / for i in 3..10 { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:5 - | -LL | / for i in 0..src.len() { -LL | | dst[count] = src[i]; -LL | | dst2[count2] = src[i]; -LL | | count += 1; -LL | | count2 += 1; -LL | | } - | |_____^ - | -help: try replacing the loop by - | -LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); - | - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:5 - | -LL | / for i in 0..1 << 1 { -LL | | dst[count] = src[i + 2]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:5 + --> $DIR/without_loop_counters.rs:120:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i].clone(); LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` -error: aborting due to 22 previous errors +error: aborting due to 13 previous errors From 101e76f117eeefcd2e901bab707de199b030f201 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 28 Sep 2020 19:14:39 +0200 Subject: [PATCH 0678/1110] needless arbitrary self: handle macros --- .../src/needless_arbitrary_self_type.rs | 30 +++++++-- tests/ui/auxiliary/proc_macro_attr.rs | 61 ++++++++++++++++++- .../needless_arbitrary_self_type_unfixable.rs | 45 ++++++++++++++ ...dless_arbitrary_self_type_unfixable.stderr | 10 +++ 4 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 tests/ui/needless_arbitrary_self_type_unfixable.rs create mode 100644 tests/ui/needless_arbitrary_self_type_unfixable.stderr diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 38bdd0f7ed2..7687962bdd9 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{in_macro, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; @@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod if let [segment] = &path.segments[..]; if segment.ident.name == kw::SelfUpper; then { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; let self_param = match (binding_mode, mutbl) { (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), (Mode::Value, Mutability::Not) => "self".to_string(), }; @@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, - Applicability::MachineApplicable, + applicability, ) } } @@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if !p.is_self() { + // Bail out if the parameter it's not a receiver or was not written by the user + if !p.is_self() || in_macro(p.span) { return; } diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index 01796d45f13..de670cdfc31 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -2,7 +2,7 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(clippy::useless_conversion)] extern crate proc_macro; @@ -12,7 +12,11 @@ extern crate syn; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::parse_macro_input; -use syn::{parse_quote, ItemTrait, TraitItem}; +use syn::spanned::Spanned; +use syn::token::Star; +use syn::{ + parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, +}; #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { @@ -36,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { } TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream { + fn make_name(count: usize) -> String { + format!("'life{}", count) + } + + fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { + let arg = sig.inputs.first_mut()?; + if let FnArg::Typed(PatType { pat, .. }) = arg { + if let Pat::Ident(PatIdent { ident, .. }) = &**pat { + if ident == "self" { + return Some(arg); + } + } + } + None + } + + let mut elided = 0; + let mut item = parse_macro_input!(input as ItemImpl); + + // Look for methods having arbitrary self type taken by &mut ref + for inner in &mut item.items { + if let ImplItem::Method(method) = inner { + if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { + if let box Type::Reference(reference) = &mut pat_type.ty { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a non-existent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); + } + } + } + } + + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs new file mode 100644 index 00000000000..a39d96109f1 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -0,0 +1,45 @@ +// aux-build:proc_macro_attr.rs + +#![warn(clippy::needless_arbitrary_self_type)] + +#[macro_use] +extern crate proc_macro_attr; + +mod issue_6089 { + // Check that we don't lint if the `self` parameter comes from expansion + + macro_rules! test_from_expansion { + () => { + trait T1 { + fn test(self: &Self); + } + + struct S1 {} + + impl T1 for S1 { + fn test(self: &Self) {} + } + }; + } + + test_from_expansion!(); + + // If only the lifetime name comes from expansion we will lint, but the suggestion will have + // placeholders and will not be applied automatically, as we can't reliably know the original name. + // This specific case happened with async_trait. + + trait T2 { + fn call_with_mut_self(&mut self); + } + + struct S2 {} + + // The method's signature will be expanded to: + // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} + #[rename_my_lifetimes] + impl T2 for S2 { + fn call_with_mut_self(self: &mut Self) {} + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/tests/ui/needless_arbitrary_self_type_unfixable.stderr new file mode 100644 index 00000000000..44a0e6ddeac --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.stderr @@ -0,0 +1,10 @@ +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31 + | +LL | fn call_with_mut_self(self: &mut Self) {} + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: aborting due to previous error + From 124420f920fd9323e69829a35876add8c358a666 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 29 Sep 2020 23:06:08 +0200 Subject: [PATCH 0679/1110] needless-lifetime / fix master merge --- tests/ui/crashes/ice-2774.stderr | 2 +- tests/ui/crashes/needless_lifetimes_impl_trait.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr index fd502cba73a..0c2d48f938f 100644 --- a/tests/ui/crashes/ice-2774.stderr +++ b/tests/ui/crashes/ice-2774.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/ice-2774.rs:17:1 + --> $DIR/ice-2774.rs:15:1 | LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 02b86397ed5..d68bbe78802 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -1,11 +1,11 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes_impl_trait.rs:17:5 + --> $DIR/needless_lifetimes_impl_trait.rs:15:5 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/needless_lifetimes_impl_trait.rs:3:9 + --> $DIR/needless_lifetimes_impl_trait.rs:1:9 | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ From aa2ac38fa7224bff68eeff8781321c9c355e5980 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 30 Sep 2020 00:08:19 +0200 Subject: [PATCH 0680/1110] needless-lifetime / add known problem item --- clippy_lints/src/lifetimes.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 1d17fbd3725..530968c191f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -26,8 +26,11 @@ declare_clippy_lint! { /// complicated, while there is nothing out of the ordinary going on. Removing /// them leads to more readable code. /// - /// **Known problems:** Potential false negatives: we bail out if the function - /// has a `where` clause where lifetimes are mentioned. + /// **Known problems:** + /// - We bail out if the function has a `where` clause where lifetimes + /// are mentioned due to potenial false positives. + /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the + /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// **Example:** /// ```rust From cb2be6f9dba0224c63180eada2d089100450391a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 30 Sep 2020 00:33:46 +0200 Subject: [PATCH 0681/1110] needless-lifetime / pr remarks --- clippy_lints/src/lifetimes.rs | 1 + tests/ui/needless_lifetimes.rs | 18 +++++++++++++++++- tests/ui/needless_lifetimes.stderr | 20 +++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 530968c191f..d7043e7bd8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -383,6 +383,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + return; }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 548c5929c61..d482d466e44 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -336,9 +336,25 @@ mod nested_elision_sites { |i| i } // lint - fn pointer_fn_elidable<'a>(f: fn(&i32) -> &i32, i: &'a i32) -> &'a i32 { + fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { f(i) } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + |f| () + } } fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index ac38ab8effd..c8a2e8b81c0 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -132,5 +132,23 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:339:5 + | +LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:352:5 + | +LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:355:5 + | +LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors From 0690f9c5d526baa593cc62b175ef5e7f2c8f4f9c Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Mon, 28 Sep 2020 15:30:47 +0200 Subject: [PATCH 0682/1110] Add lint for inline assembly syntax style preference --- CHANGELOG.md | 2 + clippy_lints/src/asm_syntax.rs | 125 +++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 7 ++ src/lintlist/mod.rs | 14 ++++ tests/ui/asm_syntax.rs | 31 ++++++++ tests/ui/asm_syntax.stderr | 44 ++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 clippy_lints/src/asm_syntax.rs create mode 100644 tests/ui/asm_syntax.rs create mode 100644 tests/ui/asm_syntax.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 575cbd60792..0de6f4b4235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1635,6 +1635,8 @@ Released 2018-09-13 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs new file mode 100644 index 00000000000..ef1f1a14afc --- /dev/null +++ b/clippy_lints/src/asm_syntax.rs @@ -0,0 +1,125 @@ +use std::fmt; + +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum AsmStyle { + Intel, + Att, +} + +impl fmt::Display for AsmStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AsmStyle::Intel => f.write_str("Intel"), + AsmStyle::Att => f.write_str("AT&T"), + } + } +} + +impl std::ops::Not for AsmStyle { + type Output = AsmStyle; + + fn not(self) -> AsmStyle { + match self { + AsmStyle::Intel => AsmStyle::Att, + AsmStyle::Att => AsmStyle::Intel, + } + } +} + +fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) { + if let ExprKind::InlineAsm(ref inline_asm) = expr.kind { + let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + AsmStyle::Att + } else { + AsmStyle::Intel + }; + + if style == check_for { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("{} x86 assembly syntax used", style), + None, + &format!("use {} x86 assembly syntax", !style), + ); + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of Intel x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for AT&T x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + pub INLINE_ASM_X86_INTEL_SYNTAX, + restriction, + "prefer AT&T x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel); + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of AT&T x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for Intel x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + pub INLINE_ASM_X86_ATT_SYNTAX, + restriction, + "prefer Intel x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86AttSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att); + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 529c2450541..10da59c7a7a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -153,6 +153,7 @@ mod utils; mod approx_const; mod arithmetic; mod as_conversions; +mod asm_syntax; mod assertions_on_constants; mod assign_ops; mod async_yields_async; @@ -487,6 +488,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, &as_conversions::AS_CONVERSIONS, + &asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, + &asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, @@ -1123,12 +1126,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); + store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); + store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), + LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 76e655ad603..16ceb617965 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -899,6 +899,20 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "inline_asm_x86_att_syntax", + group: "restriction", + desc: "prefer Intel x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, + Lint { + name: "inline_asm_x86_intel_syntax", + group: "restriction", + desc: "prefer AT&T x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, Lint { name: "inline_fn_without_body", group: "correctness", diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs new file mode 100644 index 00000000000..049bfa604d5 --- /dev/null +++ b/tests/ui/asm_syntax.rs @@ -0,0 +1,31 @@ +#![feature(asm)] +// only-x86 only-x86_64 + +#[warn(clippy::inline_asm_x86_intel_syntax)] +mod warn_intel { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +#[warn(clippy::inline_asm_x86_att_syntax)] +mod warn_att { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +fn main() { + unsafe { + warn_att::use_asm(); + warn_intel::use_asm(); + } +} diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr new file mode 100644 index 00000000000..27b51166eac --- /dev/null +++ b/tests/ui/asm_syntax.stderr @@ -0,0 +1,44 @@ +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:7:9 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:8:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:9:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:21:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:22:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 5 previous errors + From 507561ecfcec33d4263cff17cfe953518b8c3ce6 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 30 Sep 2020 23:30:49 +0200 Subject: [PATCH 0683/1110] Update tests/ui/asm_syntax.rs Use a single `only` header command in asm_syntax test --- tests/ui/asm_syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs index 049bfa604d5..658cae397e1 100644 --- a/tests/ui/asm_syntax.rs +++ b/tests/ui/asm_syntax.rs @@ -1,5 +1,5 @@ #![feature(asm)] -// only-x86 only-x86_64 +// only-x86_64 #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { From d28211ddb6bec659acd523ef63966a5032a618b7 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 1 Oct 2020 16:40:59 +0200 Subject: [PATCH 0684/1110] Fix rustup fallout --- clippy_lints/src/loops.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6c54c07869a..294f0449281 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -826,7 +826,7 @@ struct FixedOffsetVar<'hir> { } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind() { + let is_slice = match ty.kind { ty::Ref(_, subty, _) => is_slice_like(cx, subty), ty::Slice(..) | ty::Array(..) => true, _ => false, @@ -1403,7 +1403,7 @@ fn is_end_eq_array_len<'tcx>( if_chain! { if let ExprKind::Lit(ref lit) = end.kind; if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind(); + if let ty::Array(_, arr_len_const) = indexed_ty.kind; if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); then { return match limits { @@ -1640,7 +1640,7 @@ fn check_for_loop_over_map_kv<'tcx>( if let PatKind::Tuple(ref pat, _) = pat.kind { if pat.len() == 2 { let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { + let (new_pat_span, kind, ty, mutbl) = match cx.typeck_results().expr_ty(arg).kind { ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl), (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not), @@ -1968,7 +1968,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { + if let ty::Ref(_, _, mutbl) = ty.kind { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -1980,7 +1980,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { + if let ty::Ref(_, _, mutbl) = ty.kind { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -2078,7 +2078,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc - match ty.kind() { + match ty.kind { ty::Array(_, n) => n .try_eval_usize(cx.tcx, cx.param_env) .map_or(false, |val| (0..=32).contains(&val)), From 0a91fe7016cdb48d3824c218e02eca8cfdec3854 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 1 Oct 2020 23:53:05 +0900 Subject: [PATCH 0685/1110] Don't emit a lint for the suggestion leading to errors in `needless_range_loop` --- clippy_lints/src/loops.rs | 6 +++++- tests/ui/needless_range_loop2.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7f998c63f49..61b63597b16 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, @@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>( let skip = if starts_at_zero { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + return; } else { format!(".skip({})", snippet(cx, start.span, "..")) }; @@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + return; } else { match limits { ast::RangeLimits::Closed => { diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index a82b1159161..7633316e0f8 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -82,6 +82,20 @@ fn main() { for i in 1..3 { println!("{}", arr[i]); } + + // Fix #5945 + let mut vec = vec![1, 2, 3, 4]; + for i in 0..vec.len() - 1 { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() - 1 { + vec[i] += 1; + } } mod issue2277 { From 388384177e89db60280dc275e4239e97e61f6a59 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 13:30:50 +1300 Subject: [PATCH 0686/1110] document `MinifyingSugg` and `Offset` ...and also swap their position --- clippy_lints/src/loops.rs | 82 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 215700fed8c..9a1ba4907cd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -805,44 +805,10 @@ fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { } } -#[derive(Clone, Copy)] -enum OffsetSign { - Positive, - Negative, -} - -struct Offset { - value: MinifyingSugg<'static>, - sign: OffsetSign, -} - -impl Offset { - fn negative(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Negative, - } - } - - fn positive(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Positive, - } - } - - fn empty() -> Self { - Self::positive(sugg::ZERO) - } -} - -fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { - match rhs.sign { - OffsetSign::Positive => lhs + &rhs.value, - OffsetSign::Negative => lhs - &rhs.value, - } -} - +/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; +/// and also, it avoids subtracting a variable from the same one by replacing it with `0`. +/// it exists for the convenience of the overloaded operators while normal functions can do the +/// same. #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); @@ -909,6 +875,46 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { } } +/// a wrapper around `MinifyingSugg`, which carries a operator like currying +/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`). +struct Offset { + value: MinifyingSugg<'static>, + sign: OffsetSign, +} + +#[derive(Clone, Copy)] +enum OffsetSign { + Positive, + Negative, +} + +impl Offset { + fn negative(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Negative, + } + } + + fn positive(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Positive, + } + } + + fn empty() -> Self { + Self::positive(sugg::ZERO) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + #[derive(Debug, Clone, Copy)] enum StartKind<'hir> { Range, From 94d7b82340792af6e5c9b7c105dca1f2fef1b495 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:04:46 +1300 Subject: [PATCH 0687/1110] simplify the code --- clippy_lints/src/loops.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9a1ba4907cd..2ae5a9acb75 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -973,10 +973,7 @@ fn get_details_from_idx<'tcx>( ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), _ => None, }, - ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { - // `e` is always non paren as it's a `Path` - Some(Sugg::NonParen(snippet(cx, e.span, "???"))) - }, + ExprKind::Path(..) if get_start(cx, e, starts).is_none() => Some(Sugg::hir(cx, e, "???")), _ => None, } } @@ -1010,8 +1007,7 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( cx: &'a LateContext<'tcx>, - stmts: &'tcx [Stmt<'tcx>], - expr: Option<&'tcx Expr<'tcx>>, + Block { stmts, expr, .. }: &'tcx Block<'tcx>, loop_counters: &'c [Start<'tcx>], ) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { stmts @@ -1025,7 +1021,7 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( then { None } else { Some(e) } }, }) - .chain(expr.into_iter()) + .chain((*expr).into_iter()) .map(get_assignment) } @@ -1184,7 +1180,7 @@ fn detect_manual_memcpy<'tcx>( if let Some(loop_counters) = get_loop_counters(cx, block, expr) { starts.extend(loop_counters); } - iter_a = Some(get_assignments(cx, block.stmts, block.expr, &starts)); + iter_a = Some(get_assignments(cx, block, &starts)); } else { iter_b = Some(get_assignment(body)); } From e91202cf683b7265eec484ad311b5a9d3b56fc3e Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 2 Oct 2020 05:43:43 +0200 Subject: [PATCH 0688/1110] Allow exponent separator Fixes #6096 --- clippy_lints/src/utils/numeric_literal.rs | 19 +++++++++++-------- tests/ui/inconsistent_digit_grouping.fixed | 4 ++++ tests/ui/inconsistent_digit_grouping.rs | 4 ++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 5e8800d38eb..52d3c2c1daf 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent separator (b'e' or b'E') and the exponent part. - pub exponent: Option<(char, &'a str)>, + /// The exponent separator (b'e' or b'E') including preceding underscore if present + /// and the exponent part. + pub exponent: Option<(&'a str, &'a str)>, /// The type suffix, including preceding underscore if present. pub suffix: Option<&'a str>, @@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> { self.radix == Radix::Decimal } - pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { + pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { let mut integer = digits; let mut fraction = None; let mut exponent = None; @@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> { fraction = Some(&digits[i + 1..]); }, 'e' | 'E' => { - if integer.len() > i { - integer = &digits[..i]; + let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i }; + + if integer.len() > exp_start { + integer = &digits[..exp_start]; } else { - fraction = Some(&digits[integer.len() + 1..i]); + fraction = Some(&digits[integer.len() + 1..exp_start]); }; - exponent = Some((c, &digits[i + 1..])); + exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, _ => {}, @@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> { } if let Some((separator, exponent)) = self.exponent { - output.push(separator); + output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); } diff --git a/tests/ui/inconsistent_digit_grouping.fixed b/tests/ui/inconsistent_digit_grouping.fixed index b75f10917df..dd683e7f746 100644 --- a/tests/ui/inconsistent_digit_grouping.fixed +++ b/tests/ui/inconsistent_digit_grouping.fixed @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/inconsistent_digit_grouping.rs b/tests/ui/inconsistent_digit_grouping.rs index 79ce38be19b..d5d27c853c2 100644 --- a/tests/ui/inconsistent_digit_grouping.rs +++ b/tests/ui/inconsistent_digit_grouping.rs @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } From 1402d8ae4f0c07ac48bd8297ec07901346c32d32 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:18:37 +1300 Subject: [PATCH 0689/1110] fix a FN where incr exprs with no semicolon at ends --- clippy_lints/src/loops.rs | 18 ++++++++++++------ tests/ui/manual_memcpy/with_loop_counters.rs | 7 +++++++ .../ui/manual_memcpy/with_loop_counters.stderr | 11 ++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2ae5a9acb75..ea40c2af4f7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,14 +1014,20 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( .iter() .filter_map(move |stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { - if let ExprKind::AssignOp(_, var, _) = e.kind; - // skip StartKind::Range - if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); - then { None } else { Some(e) } - }, + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain((*expr).into_iter()) + .filter(move |e| { + if let ExprKind::AssignOp(_, place, _) = e.kind { + !loop_counters + .iter() + // skip StartKind::Range + .skip(1) + .any(|counter| same_var(cx, place, counter.id)) + } else { + true + } + }) .map(get_assignment) } diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index a49ba9eb10a..70873c9e994 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -59,6 +59,13 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[count] = src[i + 2]; count += 1; } + + // make sure incrementing expressions without semicolons at the end of loops are handled correctly. + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1 + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 24393ad9b4d..598c881b4d6 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -89,5 +89,14 @@ LL | | count += 1; LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` -error: aborting due to 9 previous errors +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:65:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1 +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + +error: aborting due to 10 previous errors From 41a0ccbc57902e488e62ed8ca9a1ebb565129c09 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 21:19:14 +1300 Subject: [PATCH 0690/1110] add comments around `loop_counters` --- clippy_lints/src/loops.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ea40c2af4f7..4f279cc5ef7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1005,6 +1005,10 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx } } +/// Get assignments from the given block. +/// The returned iterator yields `None` if no assignment expressions are there, +/// filtering out the increments of the given whitelisted loop counters; +/// because its job is to make sure there's nothing other than assignments and the increments. fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( cx: &'a LateContext<'tcx>, Block { stmts, expr, .. }: &'tcx Block<'tcx>, @@ -1021,7 +1025,8 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( if let ExprKind::AssignOp(_, place, _) = e.kind { !loop_counters .iter() - // skip StartKind::Range + // skip the first item which should be `StartKind::Range` + // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop. .skip(1) .any(|counter| same_var(cx, place, counter.id)) } else { @@ -1191,11 +1196,11 @@ fn detect_manual_memcpy<'tcx>( iter_b = Some(get_assignment(body)); } - // The only statements in the for loops can be indexed assignments from - // indexed retrievals. let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); From 2a0e45b8dbfa9c8494371983b128b933082a9c13 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 21:30:21 +1300 Subject: [PATCH 0691/1110] supress `clippy::filter_map` --- clippy_lints/src/loops.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4f279cc5ef7..d3a1683cc0c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,6 +1014,9 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( Block { stmts, expr, .. }: &'tcx Block<'tcx>, loop_counters: &'c [Start<'tcx>], ) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { + // As the `filter` and `map` below do different things, I think putting together + // just increases complexity. (cc #3188 and #4193) + #[allow(clippy::filter_map)] stmts .iter() .filter_map(move |stmt| match stmt.kind { From 515ca9312393bd00af0e867fceee9aff9b6e565d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 2 Oct 2020 11:54:31 +0200 Subject: [PATCH 0692/1110] Look for soft hyphens as well --- clippy_lints/src/unicode.rs | 14 +++++++------- tests/ui/unicode.rs | 2 ++ tests/ui/unicode.stderr | 14 ++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d8c57f0e7ae..d3fe60042a8 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -8,18 +8,18 @@ use rustc_span::source_map::Span; use unicode_normalization::UnicodeNormalization; declare_clippy_lint! { - /// **What it does:** Checks for the Unicode zero-width space in the code. + /// **What it does:** Checks for invisible Unicode characters in the code. /// /// **Why is this bad?** Having an invisible character in the code makes for all /// sorts of April fools, but otherwise is very much frowned upon. /// /// **Known problems:** None. /// - /// **Example:** You don't see it, but there may be a zero-width space - /// somewhere in this text. + /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen + /// some­where in this text. pub ZERO_WIDTH_SPACE, correctness, - "using a zero-width space in a string literal, which is confusing" + "using an invisible character in a string literal, which is confusing" } declare_clippy_lint! { @@ -91,14 +91,14 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.contains('\u{200B}') { + if let Some(invisible) = string.chars().find(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { span_lint_and_sugg( cx, ZERO_WIDTH_SPACE, span, - "zero-width space detected", + &format!("invisible character detected: {:?}", invisible), "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}"), + string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index 27db9594f3b..f3fd1c57da6 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -2,6 +2,8 @@ fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); + print!("Here >­< is a SHY, and ­another"); + print!("This\u{ad}is\u{ad}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 4575a132e5b..b0445b070fd 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,4 +1,4 @@ -error: zero-width space detected +error: invisible character detected: '/u{200b}' --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); @@ -6,8 +6,14 @@ LL | print!("Here >​< is a ZWS, and ​another"); | = note: `-D clippy::zero-width-space` implied by `-D warnings` +error: invisible character detected: '/u{ad}' + --> $DIR/unicode.rs:5:12 + | +LL | print!("Here >­< is a SHY, and ­another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` + error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:9:12 + --> $DIR/unicode.rs:11:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -15,12 +21,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:15:12 + --> $DIR/unicode.rs:17:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From 45f25f82fe652e073445e6f1601d25a7a292d01c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 2 Oct 2020 12:02:54 +0200 Subject: [PATCH 0693/1110] Run update_lints --- src/lintlist/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 16ceb617965..3654dbc6124 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2813,7 +2813,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "zero_width_space", group: "correctness", - desc: "using a zero-width space in a string literal, which is confusing", + desc: "using an invisible character in a string literal, which is confusing", deprecation: None, module: "unicode", }, From 7820cb14421f751c05d6d2d5925236c3429cd93f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 23:21:24 +1300 Subject: [PATCH 0694/1110] Add tests for * `dst.len()` as the end of the range with loop counters * the increment of the loop counter at the top of the loop --- tests/ui/manual_memcpy/with_loop_counters.rs | 17 +++++++++++++++++ .../ui/manual_memcpy/with_loop_counters.stderr | 17 +++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index 70873c9e994..ba388a05a28 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -37,6 +37,12 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) count += 1; } + let mut count = 2; + for i in 0..dst.len() { + dst[i] = src[count]; + count += 1; + } + let mut count = 5; for i in 3..10 { dst[i] = src[count]; @@ -66,6 +72,17 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[i] = src[count]; count += 1 } + + // make sure ones where the increment is not at the end of the loop. + // As a possible enhancement, one could adjust the offset in the suggestion according to + // the position. For example, if the increment is at the top of the loop; + // treating the loop counter as if it were initialized 1 greater than the original value. + let mut count = 0; + #[allow(clippy::needless_range_loop)] + for i in 0..src.len() { + count += 1; + dst[i] = src[count]; + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 598c881b4d6..2547b19f5d1 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -57,6 +57,15 @@ LL | | } error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:41:5 | +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:47:5 + | LL | / for i in 3..10 { LL | | dst[i] = src[count]; LL | | count += 1; @@ -64,7 +73,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:48:5 + --> $DIR/with_loop_counters.rs:54:5 | LL | / for i in 0..src.len() { LL | | dst[count] = src[i]; @@ -81,7 +90,7 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:58:5 + --> $DIR/with_loop_counters.rs:64:5 | LL | / for i in 0..1 << 1 { LL | | dst[count] = src[i + 2]; @@ -90,7 +99,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:65:5 + --> $DIR/with_loop_counters.rs:71:5 | LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; @@ -98,5 +107,5 @@ LL | | count += 1 LL | | } | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors From b54188429409a6da5f931fa4be5df1f40b377015 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 23:38:10 +1300 Subject: [PATCH 0695/1110] remove the explicit return value of `print_limit` --- clippy_lints/src/loops.rs | 41 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d3a1683cc0c..3e9265c1215 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1082,30 +1082,29 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = - |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { - 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, base); - then { - if sugg.as_str() == end_str { - sugg::EMPTY.into() - } else { - sugg - } + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { + 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, base); + then { + if sugg.as_str() == end_str { + sugg::EMPTY.into() } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &sugg::ONE.into() - }, - ast::RangeLimits::HalfOpen => sugg, - } + sugg + } + } else { + match limits { + ast::RangeLimits::Closed => { + sugg + &sugg::ONE.into() + }, + ast::RangeLimits::HalfOpen => sugg, } } - }; + } + }; let start_str = Sugg::hir(cx, start, "").into(); let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); From f302af33bc1901ddd0226c8d3a6433682a85f69b Mon Sep 17 00:00:00 2001 From: nahuakang Date: Fri, 2 Oct 2020 20:13:01 +0200 Subject: [PATCH 0696/1110] Add doc comment issue of #5834 to known problems of lint doc_markdown --- clippy_lints/src/doc.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 62bb70af06e..07f604cf714 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -32,6 +32,11 @@ declare_clippy_lint! { /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks /// for is limited, and there are still false positives. /// + /// In addition, when writing documentation comments, including `[]` brackets + /// inside a link text would trip the parser. Therfore, documenting link with + /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec + /// would fail. + /// /// **Examples:** /// ```rust /// /// Do something with the foo_bar parameter. See also @@ -39,6 +44,14 @@ declare_clippy_lint! { /// // ^ `foo_bar` and `that::other::module::foo` should be ticked. /// fn doit(foo_bar: usize) {} /// ``` + /// + /// ```rust + /// // Link text with `[]` brackets should be written as following: + /// /// Consume the array and return the inner + /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec]. + /// /// [SmallVec]: SmallVec + /// fn main() {} + /// ``` pub DOC_MARKDOWN, pedantic, "presence of `_`, `::` or camel-case outside backticks in documentation" From 8b8c63f568bc838aa7997a6933a40c3ab7b91a5d Mon Sep 17 00:00:00 2001 From: Long Louis Bui Date: Wed, 30 Sep 2020 12:13:12 -0700 Subject: [PATCH 0697/1110] changed non_copy_const lints to warn by default --- clippy_lints/src/lib.rs | 4 ++-- clippy_lints/src/non_copy_const.rs | 28 ++++++++++++++++++++++++---- src/lintlist/mod.rs | 4 ++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 10da59c7a7a..71bd7c4f82a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1604,6 +1604,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&panic_unimplemented::PANIC_PARAMS), @@ -1760,8 +1762,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index bb44eeb6adc..7b662eae775 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,6 +1,6 @@ //! Checks for uses of const which the type is not `Freeze` (`Cell`-free). //! -//! This lint is **deny** by default. +//! This lint is **warn** by default. use std::ptr; @@ -17,6 +17,8 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::{in_constant, qpath_res, span_lint_and_then}; use if_chain::if_chain; +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). @@ -34,6 +36,15 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// + /// When an enum has variants with interior mutability, use of its non interior mutable + /// variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) + /// /// **Example:** /// ```rust /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; @@ -49,10 +60,12 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub DECLARE_INTERIOR_MUTABLE_CONST, - correctness, + style, "declaring `const` with interior mutability" } +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks if `const` items which is interior mutable (e.g., /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. @@ -64,7 +77,14 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// **Known problems:** None + /// **Known problems:** When an enum has variants with interior mutability, use of its non + /// interior mutable variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -81,7 +101,7 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub BORROW_INTERIOR_MUTABLE_CONST, - correctness, + style, "referencing `const` with interior mutability" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 16ceb617965..ccdfac3a34b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -110,7 +110,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "borrow_interior_mutable_const", - group: "correctness", + group: "style", desc: "referencing `const` with interior mutability", deprecation: None, module: "non_copy_const", @@ -334,7 +334,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "declare_interior_mutable_const", - group: "correctness", + group: "style", desc: "declaring `const` with interior mutability", deprecation: None, module: "non_copy_const", From 998bd3b6b4d168099346e460ae42897dc3667882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 3 Oct 2020 00:03:33 +0200 Subject: [PATCH 0698/1110] Rename lint to invisible_characters --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 7 ++++--- clippy_lints/src/unicode.rs | 10 +++++----- src/lintlist/mod.rs | 14 +++++++------- tests/ui/unicode.rs | 2 +- tests/ui/unicode.stderr | 6 +++--- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de6f4b4235..617bf32f463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1647,6 +1647,7 @@ Released 2018-09-13 [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop @@ -1922,6 +1923,5 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr -[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 10da59c7a7a..91244ec2724 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -854,9 +854,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, - &unicode::ZERO_WIDTH_SPACE, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, @@ -1511,7 +1511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1779,7 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1910,6 +1910,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); + ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d3fe60042a8..d6c8d317dc2 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen /// some­where in this text. - pub ZERO_WIDTH_SPACE, + pub INVISIBLE_CHARACTERS, correctness, "using an invisible character in a string literal, which is confusing" } @@ -63,7 +63,7 @@ declare_clippy_lint! { "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" } -declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); +declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -91,12 +91,12 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if let Some(invisible) = string.chars().find(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { + if string.chars().any(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { span_lint_and_sugg( cx, - ZERO_WIDTH_SPACE, + INVISIBLE_CHARACTERS, span, - &format!("invisible character detected: {:?}", invisible), + "invisible character detected", "consider replacing the string with", string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), Applicability::MachineApplicable, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3654dbc6124..e7df733d3a2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -969,6 +969,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "invisible_characters", + group: "correctness", + desc: "using an invisible character in a string literal, which is confusing", + deprecation: None, + module: "unicode", + }, Lint { name: "items_after_statements", group: "pedantic", @@ -2810,13 +2817,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, - Lint { - name: "zero_width_space", - group: "correctness", - desc: "using an invisible character in a string literal, which is confusing", - deprecation: None, - module: "unicode", - }, Lint { name: "zst_offset", group: "correctness", diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index f3fd1c57da6..b6944e04859 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,4 +1,4 @@ -#[warn(clippy::zero_width_space)] +#[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index b0445b070fd..595d80ea279 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,12 +1,12 @@ -error: invisible character detected: '/u{200b}' +error: invisible character detected --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` | - = note: `-D clippy::zero-width-space` implied by `-D warnings` + = note: `-D clippy::invisible-characters` implied by `-D warnings` -error: invisible character detected: '/u{ad}' +error: invisible character detected --> $DIR/unicode.rs:5:12 | LL | print!("Here >­< is a SHY, and ­another"); From 572e4c4837e5f955cdc3751b9ad63f0bfb86beac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 3 Oct 2020 00:07:56 +0200 Subject: [PATCH 0699/1110] Add WJ --- clippy_lints/src/unicode.rs | 7 +++++-- tests/ui/unicode.rs | 2 ++ tests/ui/unicode.stderr | 12 +++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d6c8d317dc2..93d59cc7fcd 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -91,14 +91,17 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.chars().any(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { + if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { span_lint_and_sugg( cx, INVISIBLE_CHARACTERS, span, "invisible character detected", "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), + string + .replace("\u{200B}", "\\u{200B}") + .replace("\u{ad}", "\\u{AD}") + .replace("\u{2060}", "\\u{2060}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index b6944e04859..1f596c312fe 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -4,6 +4,8 @@ fn zero() { print!("This\u{200B}is\u{200B}fine"); print!("Here >­< is a SHY, and ­another"); print!("This\u{ad}is\u{ad}fine"); + print!("Here >⁠< is a WJ, and ⁠another"); + print!("This\u{2060}is\u{2060}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 595d80ea279..3fca463c620 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -12,8 +12,14 @@ error: invisible character detected LL | print!("Here >­< is a SHY, and ­another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` +error: invisible character detected + --> $DIR/unicode.rs:7:12 + | +LL | print!("Here >⁠< is a WJ, and ⁠another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` + error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:11:12 + --> $DIR/unicode.rs:13:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -21,12 +27,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:17:12 + --> $DIR/unicode.rs:19:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From be8160878ab7a8e1367acecc173f92e6f509e033 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 1 Oct 2020 10:34:53 +1300 Subject: [PATCH 0700/1110] split `interior_mutable_const` tests and clean it * remove a 'ERROR' comment from `borrow` `Vec` itself is `Freeze` as it holds the atomic in heap * remove `ONCE_INIT` from `declare` it seems like an artifact from previous spliting --- .../others.rs} | 33 +-- .../others.stderr} | 58 ++--- .../borrow_interior_mutable_const/traits.rs | 202 ++++++++++++++++++ .../traits.stderr | 123 +++++++++++ .../declare_interior_mutable_const/others.rs | 34 +++ .../others.stderr | 39 ++++ .../traits.rs} | 24 --- .../traits.stderr} | 63 ++---- 8 files changed, 431 insertions(+), 145 deletions(-) rename tests/ui/{borrow_interior_mutable_const.rs => borrow_interior_mutable_const/others.rs} (81%) rename tests/ui/{borrow_interior_mutable_const.stderr => borrow_interior_mutable_const/others.stderr} (68%) create mode 100644 tests/ui/borrow_interior_mutable_const/traits.rs create mode 100644 tests/ui/borrow_interior_mutable_const/traits.stderr create mode 100644 tests/ui/declare_interior_mutable_const/others.rs create mode 100644 tests/ui/declare_interior_mutable_const/others.stderr rename tests/ui/{declare_interior_mutable_const.rs => declare_interior_mutable_const/traits.rs} (84%) rename tests/ui/{declare_interior_mutable_const.stderr => declare_interior_mutable_const/traits.stderr} (56%) diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const/others.rs similarity index 81% rename from tests/ui/borrow_interior_mutable_const.rs rename to tests/ui/borrow_interior_mutable_const/others.rs index 9fcc9ece49b..ea25729d11d 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -19,33 +19,7 @@ const NO_ANN: &dyn Display = &70; static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); const ONCE_INIT: Once = Once::new(); -trait Trait { - type AssocType; - - const ATOMIC: AtomicUsize; - const INPUT: T; - const ASSOC: Self::AssocType; - - fn function() { - let _ = &Self::INPUT; - let _ = &Self::ASSOC; - } -} - -impl Trait for u64 { - type AssocType = AtomicUsize; - - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INPUT: u32 = 10; - const ASSOC: Self::AssocType = AtomicUsize::new(11); - - fn function() { - let _ = &Self::INPUT; - let _ = &Self::ASSOC; //~ ERROR interior mutability - } -} - -// This is just a pointer that can be safely dereferended, +// This is just a pointer that can be safely dereferenced, // it's semantically the same as `&'static T`; // but it isn't allowed to make a static reference from an arbitrary integer value at the moment. // For more information, please see the issue #5918. @@ -100,7 +74,7 @@ fn main() { let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability - let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability + let _ = &*ATOMIC_TUPLE.1; let _ = &ATOMIC_TUPLE.2; let _ = (&&&&ATOMIC_TUPLE).0; let _ = (&&&&ATOMIC_TUPLE).2; @@ -124,9 +98,6 @@ fn main() { assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); assert!(STATIC_TUPLE.1.is_empty()); - u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability - assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability - assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. let _ = &CELL_REF.0; diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr similarity index 68% rename from tests/ui/borrow_interior_mutable_const.stderr rename to tests/ui/borrow_interior_mutable_const/others.stderr index ed726a6b46e..9a908cf30e9 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,22 +1,14 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:44:18 + --> $DIR/others.rs:54:5 | -LL | let _ = &Self::ASSOC; //~ ERROR interior mutability - | ^^^^^^^^^^^ +LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^ | = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:80:5 - | -LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:16 + --> $DIR/others.rs:55:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -24,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:84:22 + --> $DIR/others.rs:58:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:85:25 + --> $DIR/others.rs:59:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:86:27 + --> $DIR/others.rs:60:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:87:26 + --> $DIR/others.rs:61:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -56,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:98:14 + --> $DIR/others.rs:72:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:99:14 + --> $DIR/others.rs:73:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:100:19 + --> $DIR/others.rs:74:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:101:14 + --> $DIR/others.rs:75:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:102:13 + --> $DIR/others.rs:76:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:108:13 + --> $DIR/others.rs:82:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -104,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:113:5 + --> $DIR/others.rs:87:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -112,28 +104,12 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:114:16 + --> $DIR/others.rs:88:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ | = help: assign this const to a local or static variable, and use the variable here -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:127:5 - | -LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:128:16 - | -LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 17 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs new file mode 100644 index 00000000000..06b5d62e8f9 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/traits.rs @@ -0,0 +1,202 @@ +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] + +// this file replicates its `declare` counterpart. Please see it for more discussions. + +use std::borrow::Cow; +use std::cell::Cell; +use std::sync::atomic::{AtomicUsize, Ordering}; + +trait ConcreteTypes { + const ATOMIC: AtomicUsize; + const STRING: String; + + fn function() { + let _ = &Self::ATOMIC; //~ ERROR interior mutable + let _ = &Self::STRING; + } +} + +impl ConcreteTypes for u64 { + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const STRING: String = String::new(); + + fn function() { + // Lint this again since implementers can choose not to borrow it. + let _ = &Self::ATOMIC; //~ ERROR interior mutable + let _ = &Self::STRING; + } +} + +// a helper trait used below +trait ConstDefault { + const DEFAULT: Self; +} + +trait GenericTypes { + const TO_REMAIN_GENERIC: T; + const TO_BE_CONCRETE: U; + + fn function() { + let _ = &Self::TO_REMAIN_GENERIC; + } +} + +impl GenericTypes for Vec { + const TO_REMAIN_GENERIC: T = T::DEFAULT; + const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); + + fn function() { + let _ = &Self::TO_REMAIN_GENERIC; + let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable + } +} + +// a helper type used below +pub struct Wrapper(T); + +trait AssocTypes { + type ToBeFrozen; + type ToBeUnfrozen; + type ToBeGenericParam; + + const TO_BE_FROZEN: Self::ToBeFrozen; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen; + const WRAPPED_TO_BE_UNFROZEN: Wrapper; + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; + + fn function() { + let _ = &Self::TO_BE_FROZEN; + let _ = &Self::WRAPPED_TO_BE_UNFROZEN; + } +} + +impl AssocTypes for Vec { + type ToBeFrozen = u16; + type ToBeUnfrozen = AtomicUsize; + type ToBeGenericParam = T; + + const TO_BE_FROZEN: Self::ToBeFrozen = 12; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); + const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); + + fn function() { + let _ = &Self::TO_BE_FROZEN; + let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable + let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable + let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; + } +} + +// a helper trait used below +trait AssocTypesHelper { + type NotToBeBounded; + type ToBeBounded; + + const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; +} + +trait AssocTypesFromGenericParam +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded; + const BOUNDED: T::ToBeBounded; + + fn function() { + let _ = &Self::NOT_BOUNDED; + let _ = &Self::BOUNDED; //~ ERROR interior mutable + } +} + +impl AssocTypesFromGenericParam for Vec +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); + + fn function() { + let _ = &Self::NOT_BOUNDED; + let _ = &Self::BOUNDED; //~ ERROR interior mutable + } +} + +trait SelfType: Sized { + const SELF: Self; + const WRAPPED_SELF: Option; + + fn function() { + let _ = &Self::SELF; + let _ = &Self::WRAPPED_SELF; + } +} + +impl SelfType for u64 { + const SELF: Self = 16; + const WRAPPED_SELF: Option = Some(20); + + fn function() { + let _ = &Self::SELF; + let _ = &Self::WRAPPED_SELF; + } +} + +impl SelfType for AtomicUsize { + const SELF: Self = AtomicUsize::new(17); + const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); + + fn function() { + let _ = &Self::SELF; //~ ERROR interior mutable + let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable + } +} + +trait BothOfCellAndGeneric { + const DIRECT: Cell; + const INDIRECT: Cell<*const T>; + + fn function() { + let _ = &Self::DIRECT; + let _ = &Self::INDIRECT; //~ ERROR interior mutable + } +} + +impl BothOfCellAndGeneric for Vec { + const DIRECT: Cell = Cell::new(T::DEFAULT); + const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); + + fn function() { + let _ = &Self::DIRECT; + let _ = &Self::INDIRECT; //~ ERROR interior mutable + } +} + +struct Local(T); + +impl Local +where + T: ConstDefault + AssocTypesHelper, +{ + const ATOMIC: AtomicUsize = AtomicUsize::new(18); + const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); + + const GENERIC_TYPE: T = T::DEFAULT; + + const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); + + fn function() { + let _ = &Self::ATOMIC; //~ ERROR interior mutable + let _ = &Self::COW; + let _ = &Self::GENERIC_TYPE; + let _ = &Self::ASSOC_TYPE; + let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable + } +} + +fn main() { + u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability +} diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr new file mode 100644 index 00000000000..8f26403abd3 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/traits.stderr @@ -0,0 +1,123 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:15:18 + | +LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable + | ^^^^^^^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:26:18 + | +LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:51:18 + | +LL | let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:86:18 + | +LL | let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:87:18 + | +LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:109:18 + | +LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable + | ^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:122:18 + | +LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable + | ^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:151:18 + | +LL | let _ = &Self::SELF; //~ ERROR interior mutable + | ^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:152:18 + | +LL | let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:162:18 + | +LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:172:18 + | +LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:191:18 + | +LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:195:18 + | +LL | let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:200:5 + | +LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:201:16 + | +LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 15 previous errors + diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs new file mode 100644 index 00000000000..48c5e9537d6 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/others.rs @@ -0,0 +1,34 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::fmt::Display; +use std::sync::atomic::AtomicUsize; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable +const CELL: Cell = Cell::new(6); //~ ERROR interior mutable +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); +//~^ ERROR interior mutable + +macro_rules! declare_const { + ($name:ident: $ty:ty = $e:expr) => { + const $name: $ty = $e; + }; +} +declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + +// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. + +const INTEGER: u8 = 8; +const STRING: String = String::new(); +const STR: &str = "012345"; +const COW: Cow = Cow::Borrowed("abcdef"); +//^ note: a const item of Cow is used in the `postgres` package. + +const NO_ANN: &dyn Display = &70; + +static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); +//^ there should be no lints on this line + +fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr new file mode 100644 index 00000000000..6153c96edc4 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/others.stderr @@ -0,0 +1,39 @@ +error: a `const` item should never be interior mutable + --> $DIR/others.rs:9:1 + | +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/others.rs:10:1 + | +LL | const CELL: Cell = Cell::new(6); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/others.rs:11:1 + | +LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/others.rs:16:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const/traits.rs similarity index 84% rename from tests/ui/declare_interior_mutable_const.rs rename to tests/ui/declare_interior_mutable_const/traits.rs index 3afcdca2f04..535147ccc64 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const/traits.rs @@ -2,37 +2,13 @@ use std::borrow::Cow; use std::cell::Cell; -use std::fmt::Display; use std::sync::atomic::AtomicUsize; -use std::sync::Once; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable -const CELL: Cell = Cell::new(6); //~ ERROR interior mutable -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); -//~^ ERROR interior mutable macro_rules! declare_const { ($name:ident: $ty:ty = $e:expr) => { const $name: $ty = $e; }; } -declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable - -// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. - -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -//^ note: a const item of Cow is used in the `postgres` package. - -const NO_ANN: &dyn Display = &70; - -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -//^ there should be no lints on this line - -#[allow(clippy::declare_interior_mutable_const)] -const ONCE_INIT: Once = Once::new(); // a constant whose type is a concrete type should be linted at the definition site. trait ConcreteTypes { diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr similarity index 56% rename from tests/ui/declare_interior_mutable_const.stderr rename to tests/ui/declare_interior_mutable_const/traits.stderr index 5cb10be88d8..bb77f39b62c 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const/traits.stderr @@ -1,48 +1,13 @@ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:9:1 + --> $DIR/traits.rs:15:5 | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) +LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:10:1 - | -LL | const CELL: Cell = Cell::new(6); //~ ERROR interior mutable - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:11:1 - | -LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:16:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable - | ------------------------------------------ in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:39:5 - | -LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:16:9 + --> $DIR/traits.rs:9:9 | LL | const $name: $ty = $e; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -53,58 +18,58 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR i = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:67:5 + --> $DIR/traits.rs:43:5 | LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:92:5 + --> $DIR/traits.rs:68:5 | LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:93:5 + --> $DIR/traits.rs:69:5 | LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:112:5 + --> $DIR/traits.rs:88:5 | LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:140:5 + --> $DIR/traits.rs:116:5 | LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:141:5 + --> $DIR/traits.rs:117:5 | LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:149:5 + --> $DIR/traits.rs:125:5 | LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:165:5 + --> $DIR/traits.rs:141:5 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:171:5 + --> $DIR/traits.rs:147:5 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 11 previous errors From f58a1695a67e2db741b7237992e696ff9a14d0ab Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 1 Oct 2020 10:51:49 +1300 Subject: [PATCH 0701/1110] fix a FP in `interior_mutable_const` fix a false positive in two `interior_mutable_const` lints where a constant with enums gets linted even if it uses a clearly unfrozen variant. Note that the code uses the MIR interpreter, which the author of #3962 thought unlikely to be a solution. This might be over-engineering; but, I think it's important to be able to work with the 'http' crate (#3825). --- clippy_lints/src/non_copy_const.rs | 170 ++++++++++++++---- .../auxiliary/helper.rs | 16 ++ .../ui/borrow_interior_mutable_const/enums.rs | 101 +++++++++++ .../enums.stderr | 75 ++++++++ .../declare_interior_mutable_const/enums.rs | 123 +++++++++++++ .../enums.stderr | 89 +++++++++ 6 files changed, 539 insertions(+), 35 deletions(-) create mode 100644 tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs create mode 100644 tests/ui/borrow_interior_mutable_const/enums.rs create mode 100644 tests/ui/borrow_interior_mutable_const/enums.stderr create mode 100644 tests/ui/declare_interior_mutable_const/enums.rs create mode 100644 tests/ui/declare_interior_mutable_const/enums.stderr diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 7b662eae775..6b0d198edcf 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -5,11 +5,15 @@ use std::ptr; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + BodyId, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, +}; use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{AssocKind, Ty}; +use rustc_middle::ty::{self, AssocKind, Const, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -36,14 +40,17 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// - /// When an enum has variants with interior mutability, use of its non interior mutable - /// variants can generate false positives. See issue - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// Even though the lint avoids triggering on a constant whose type has enums that have variants + /// with interior mutability, and its value uses non interior mutable variants (see + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); + /// it complains about associated constants without default values only based on its types; + /// which might not be preferable. + /// There're other enums plus associated constants cases that the lint cannot handle. /// /// Types that have underlying or potential interior mutability trigger the lint whether /// the interior mutable field is used or not. See issues /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -105,6 +112,79 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } +fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) +} + +fn is_value_unfrozen_raw<'tcx>( + cx: &LateContext<'tcx>, + result: Result, ErrorHandled>, + ty: Ty<'tcx>, +) -> bool { + fn inner<'tcx>(cx: &LateContext<'tcx>, val: &'tcx Const<'tcx>) -> bool { + match val.ty.kind() { + // the fact that we have to dig into every structs to search enums + // leads us to the point checking `UnsafeCell` directly is the only option. + ty::Adt(ty_def, ..) if Some(ty_def.did) == cx.tcx.lang_items().unsafe_cell_type() => true, + ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { + let val = cx.tcx.destructure_const(cx.param_env.and(val)); + val.fields.iter().any(|field| inner(cx, field)) + }, + _ => false, + } + } + + result.map_or_else( + |err| { + // Consider `TooGeneric` cases as being unfrozen. + // This causes a false positive where an assoc const whose type is unfrozen + // have a value that is a frozen variant with a generic param (an example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). + // However, it prevents a number of false negatives that is, I think, important: + // 1. assoc consts in trait defs referring to consts of themselves + // (an example is `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). + // 2. a path expr referring to assoc consts whose type is doesn't have + // any frozen variants in trait defs (i.e. without substitute for `Self`). + // (e.g. borrowing `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) + // 3. similar to the false positive above; + // but the value is an unfrozen variant, or the type has no enums. (An example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` + // and `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). + // One might be able to prevent these FNs correctly, and replace this with `false`; + // e.g. implementing `has_frozen_variant` described above, and not running this function + // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd + // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, + // similar to 2., but with the a frozen variant) (e.g. borrowing + // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). + // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). + err == ErrorHandled::TooGeneric + }, + |val| inner(cx, Const::from_value(cx.tcx, val, ty)), + ) +} + +fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { + let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id()); + is_value_unfrozen_raw(cx, result, ty) +} + +fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { + let substs = cx.typeck_results().node_substs(hir_id); + + let result = cx + .tcx + .const_eval_resolve(cx.param_env, ty::WithOptConstParam::unknown(def_id), substs, None, None); + is_value_unfrozen_raw(cx, result, ty) +} + #[derive(Copy, Clone)] enum Source { Item { item: Span }, @@ -130,19 +210,7 @@ impl Source { } } -fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { - // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, - // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is - // 'unfrozen'. However, this code causes a false negative in which - // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. - // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` - // since it works when a pointer indirection involves (`Cell<*const T>`). - // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; - // but I'm not sure whether it's a decent way, if possible. - if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) { - return; - } - +fn lint(cx: &LateContext<'_>, source: Source) { let (lint, msg, span) = source.lint(); span_lint_and_then(cx, lint, span, msg, |diag| { if span.from_expansion() { @@ -165,24 +233,44 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(hir_ty, ..) = &it.kind { + if let ItemKind::Const(hir_ty, body_id) = it.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound(cx, ty, Source::Item { item: it.span }); + + if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { + lint(cx, Source::Item { item: it.span }); + } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { + if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); + // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span }); + if is_unfrozen(cx, normalized) + // When there's no default value, lint it only according to its type; + // in other words, lint consts whose value *could* be unfrozen, not definitely is. + // This feels inconsistent with how the lint treats generic types, + // which avoids linting types which potentially become unfrozen. + // One could check whether a unfrozen type have a *frozen variant* + // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), + // and do the same as the case of generic types at impl items. + // Note that it isn't sufficient to check if it has an enum + // since all of that enum's variants can be unfrozen: + // i.e. having an enum doesn't necessary mean a type has a frozen variant. + // And, implementing it isn't a trivial task; it'll probably end up + // re-implementing the trait predicate evaluation specific to `Freeze`. + && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized)) + { + lint(cx, Source::Assoc { item: trait_item.span }); + } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { + if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind { let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); let item = cx.tcx.hir().expect_item(item_hir_id); @@ -209,16 +297,23 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { ), )) .is_err(); + // If there were a function like `has_frozen_variant` described above, + // we should use here as a frozen variant is a potential to be frozen + // similar to unknown layouts. + // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` then { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound( - cx, - normalized, - Source::Assoc { - item: impl_item.span, - }, - ); + if is_unfrozen(cx, normalized) + && is_value_unfrozen_poly(cx, *body_id, normalized) + { + lint( + cx, + Source::Assoc { + item: impl_item.span, + }, + ); + } } } }, @@ -226,7 +321,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let ty = hir_ty_to_ty(cx.tcx, hir_ty); // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span }); + + if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) { + lint(cx, Source::Assoc { item: impl_item.span }); + } }, _ => (), } @@ -241,8 +339,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - match qpath_res(cx, qpath, expr.hir_id) { - Res::Def(DefKind::Const | DefKind::AssocConst, _) => {}, + let item_def_id = match qpath_res(cx, qpath, expr.hir_id) { + Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, _ => return, }; @@ -319,7 +417,9 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.typeck_results().expr_ty(dereferenced_expr) }; - verify_ty_bound(cx, ty, Source::Expr { expr: expr.span }); + if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) { + lint(cx, Source::Expr { expr: expr.span }); + } } } } diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs new file mode 100644 index 00000000000..2289f7875f0 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs @@ -0,0 +1,16 @@ +// this file solely exists to test constants defined in foreign crates. +// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure. + +#![allow(clippy::declare_interior_mutable_const)] + +use std::sync::atomic::AtomicUsize; + +enum Private { + ToBeUnfrozen(T), + Frozen(usize), +} + +pub struct Wrapper(Private); + +pub const WRAPPED_PRIVATE_UNFROZEN_VARIANT: Wrapper = Wrapper(Private::ToBeUnfrozen(AtomicUsize::new(6))); +pub const WRAPPED_PRIVATE_FROZEN_VARIANT: Wrapper = Wrapper(Private::Frozen(7)); diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs new file mode 100644 index 00000000000..5027db44561 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/enums.rs @@ -0,0 +1,101 @@ +// aux-build:helper.rs + +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] + +// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. + +extern crate helper; + +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +enum OptionalCell { + Unfrozen(Cell), + Frozen, +} + +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +fn borrow_optional_cell() { + let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &FROZEN_VARIANT; +} + +trait AssocConsts { + const TO_BE_UNFROZEN_VARIANT: OptionalCell; + const TO_BE_FROZEN_VARIANT: OptionalCell; + + const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + fn function() { + // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` + // caused by a similar reason to unfrozen types without any default values + // get linted even if it has frozen variants'. + let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable + + // The lint ignores default values because an impl of this trait can set + // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable + } +} + +impl AssocConsts for u64 { + const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + fn function() { + let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &::TO_BE_FROZEN_VARIANT; + let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; + } +} + +trait AssocTypes { + type ToBeUnfrozen; + + const TO_BE_UNFROZEN_VARIANT: Option; + const TO_BE_FROZEN_VARIANT: Option; + + // there's no need to test here because it's the exactly same as `trait::AssocTypes` + fn function(); +} + +impl AssocTypes for u64 { + type ToBeUnfrozen = AtomicUsize; + + const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: Option = None; + + fn function() { + let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &::TO_BE_FROZEN_VARIANT; + } +} + +enum BothOfCellAndGeneric { + Unfrozen(Cell<*const T>), + Generic(*const T), + Frozen(usize), +} + +impl BothOfCellAndGeneric { + const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); + + fn function() { + let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability + let _ = &Self::FROZEN_VARIANT; + } +} + +fn main() { + // constants defined in foreign crates + let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; +} diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr new file mode 100644 index 00000000000..654a1ee7df6 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/enums.stderr @@ -0,0 +1,75 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:22:14 + | +LL | let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:37:18 + | +LL | let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:41:18 + | +LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:50:18 + | +LL | let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:52:18 + | +LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:74:18 + | +LL | let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:91:18 + | +LL | let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:92:18 + | +LL | let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:99:14 + | +LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 9 previous errors + diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs new file mode 100644 index 00000000000..f44518694b8 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/enums.rs @@ -0,0 +1,123 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +enum OptionalCell { + Unfrozen(Cell), + Frozen, +} + +// a constant with enums should be linted only when the used variant is unfrozen (#3962). +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +const fn unfrozen_variant() -> OptionalCell { + OptionalCell::Unfrozen(Cell::new(false)) +} + +const fn frozen_variant() -> OptionalCell { + OptionalCell::Frozen +} + +const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable +const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); + +enum NestedInnermost { + Unfrozen(AtomicUsize), + Frozen, +} + +struct NestedInner { + inner: NestedInnermost, +} + +enum NestedOuter { + NestedInner(NestedInner), + NotNested(usize), +} + +struct NestedOutermost { + outer: NestedOuter, +} + +// a constant with enums should be linted according to its value, no matter how structs involve. +const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + outer: NestedOuter::NestedInner(NestedInner { + inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), + }), +}; //~ ERROR interior mutable +const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { + outer: NestedOuter::NestedInner(NestedInner { + inner: NestedInnermost::Frozen, + }), +}; + +trait AssocConsts { + // When there's no default value, lint it only according to its type. + // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). + const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + + // Lint default values accordingly. + const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; +} + +// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it +// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. +impl AssocConsts for u64 { + const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + // even if this sets an unfrozen variant, the lint ignores it. + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); +} + +// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters +// here are values; and I think substituted generics at definitions won't appear in MIR. +trait AssocTypes { + type ToBeUnfrozen; + + const TO_BE_UNFROZEN_VARIANT: Option; + const TO_BE_FROZEN_VARIANT: Option; +} + +impl AssocTypes for u64 { + type ToBeUnfrozen = AtomicUsize; + + const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: Option = None; +} + +// Use raw pointers since direct generics have a false negative at the type level. +enum BothOfCellAndGeneric { + Unfrozen(Cell<*const T>), + Generic(*const T), + Frozen(usize), +} + +impl BothOfCellAndGeneric { + const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + + // This is a false positive. The argument about this is on `is_value_unfrozen_raw` + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); + + // This is what is likely to be a false negative when one tries to fix + // the `GENERIC_VARIANT` false positive. + const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable +} + +// associated types here is basically the same as the one above. +trait BothOfCellAndGenericWithAssocType { + type AssocType; + + const UNFROZEN_VARIANT: BothOfCellAndGeneric = + BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); +} + +fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr new file mode 100644 index 00000000000..84198d54615 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/enums.stderr @@ -0,0 +1,89 @@ +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:12:1 + | +LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:23:1 + | +LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:45:1 + | +LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + | ^---- + | | + | _make this a static item (maybe with lazy_static) + | | +LL | | outer: NestedOuter::NestedInner(NestedInner { +LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), +LL | | }), +LL | | }; //~ ERROR interior mutable + | |__^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:59:5 + | +LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:60:5 + | +LL | const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:63:5 + | +LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:89:5 + | +LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:101:5 + | +LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mut... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:104:5 + | +LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:110:5 + | +LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:117:5 + | +LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = +LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + | |____________________________________________________________________^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:119:5 + | +LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mu... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + From 78695bd4968d45ef6afd3433af4bf6f310128770 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 5 Oct 2020 12:08:57 +0200 Subject: [PATCH 0702/1110] Do not lint float fractions in `mistyped_literal_suffixes` (fixes #4706) --- clippy_lints/src/literal_representation.rs | 9 +++------ tests/ui/mistyped_literal_suffix.fixed | 6 +++--- tests/ui/mistyped_literal_suffix.rs | 6 +++--- tests/ui/mistyped_literal_suffix.stderr | 14 +------------- 4 files changed, 10 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index a36fdca5d5d..c54103b25c2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,13 +264,10 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') + } else if num_lit.fraction.is_some() { + (&mut num_lit.integer, &["32", "64"][..], 'f') } else { - num_lit - .fraction - .as_mut() - .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| { - (fraction, &["32", "64"][..], 'f') - }) + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') }; let mut split = part.rsplit('_'); diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index baee7735730..8275bfa5db7 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] fn main() { let fail14 = 2_i32; @@ -12,13 +12,13 @@ fn main() { let fail20 = 2_i8; // let fail21 = 4_i16; // - let fail24 = 12.34_f64; + let ok24 = 12.34_64; let fail25 = 1E2_f32; let fail26 = 43E7_f64; let fail27 = 243E17_f32; #[allow(overflowing_literals)] let fail28 = 241_251_235E723_f64; - let fail29 = 42_279.911_f32; + let ok29 = 42279.911_32; let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 6de447f4021..8a451807aeb 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] fn main() { let fail14 = 2_32; @@ -12,13 +12,13 @@ fn main() { let fail20 = 2__8; // let fail21 = 4___16; // - let fail24 = 12.34_64; + let ok24 = 12.34_64; let fail25 = 1E2_32; let fail26 = 43E7_64; let fail27 = 243E17_32; #[allow(overflowing_literals)] let fail28 = 241251235E723_64; - let fail29 = 42279.911_32; + let ok29 = 42279.911_32; let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index 48a7ae90494..f4c5adfe82c 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -36,12 +36,6 @@ error: mistyped literal suffix LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 - | -LL | let fail24 = 12.34_64; - | ^^^^^^^^ help: did you mean to write: `12.34_f64` - error: mistyped literal suffix --> $DIR/mistyped_literal_suffix.rs:16:18 | @@ -66,17 +60,11 @@ error: mistyped literal suffix LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:21:18 - | -LL | let fail29 = 42279.911_32; - | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` - error: mistyped literal suffix --> $DIR/mistyped_literal_suffix.rs:23:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors From 428ef362d6901029bbf945d5f440f4122ebcece6 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 5 Oct 2020 12:23:01 +0200 Subject: [PATCH 0703/1110] Fix test formatting --- tests/ui/mistyped_literal_suffix.fixed | 7 ++++++- tests/ui/mistyped_literal_suffix.rs | 7 ++++++- tests/ui/mistyped_literal_suffix.stderr | 22 +++++++++++----------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index 8275bfa5db7..70cdb067d91 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] +#![allow( + dead_code, + unused_variables, + clippy::excessive_precision, + clippy::inconsistent_digit_grouping +)] fn main() { let fail14 = 2_i32; diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 8a451807aeb..729990af399 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] +#![allow( + dead_code, + unused_variables, + clippy::excessive_precision, + clippy::inconsistent_digit_grouping +)] fn main() { let fail14 = 2_32; diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index f4c5adfe82c..b338b8aa622 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:6:18 + --> $DIR/mistyped_literal_suffix.rs:11:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,61 +7,61 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:7:18 + --> $DIR/mistyped_literal_suffix.rs:12:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:8:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:9:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:17:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:18:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:16:18 + --> $DIR/mistyped_literal_suffix.rs:21:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:17:18 + --> $DIR/mistyped_literal_suffix.rs:22:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:20:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:13 + --> $DIR/mistyped_literal_suffix.rs:28:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` From c5f17002e5eccd4e6c44b286cd85cdcd9f4e2a2d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 5 Oct 2020 16:51:17 +0200 Subject: [PATCH 0704/1110] Add changelog for 1.48 beta --- CHANGELOG.md | 130 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 617bf32f463..a6631e6d546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,135 @@ document. ## Unreleased / In Rust Nightly -[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master) +[e636b88...master](https://github.com/rust-lang/rust-clippy/compare/e636b88...master) + +## Rust 1.48 + +Current beta, release 2020-11-19 + +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88) + +### New lints + +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894) +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720) +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038) +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044) +* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) +* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) + +### Moves and Deprecations + +* Downgrade [`verbose_bit_mask`] to pedantic + [#6036](https://github.com/rust-lang/rust-clippy/pull/6036) + +### Enhancements + +* Extend [`precedence`] to handle chains of methods combined with unary negation + [#5928](https://github.com/rust-lang/rust-clippy/pull/5928) +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack + [#5907](https://github.com/rust-lang/rust-clippy/pull/5907) +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr` + [#5884](https://github.com/rust-lang/rust-clippy/pull/5884) +* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update` + [#6025](https://github.com/rust-lang/rust-clippy/pull/6025) +* Avoid [`redundant_pattern_matching`] triggering in macros + [#6069](https://github.com/rust-lang/rust-clippy/pull/6069) +* [`option_if_let_else`]: distinguish pure from impure `else` expressions + [#5937](https://github.com/rust-lang/rust-clippy/pull/5937) +* [`needless_doctest_main`]: parse doctests instead of using textual search + [#5912](https://github.com/rust-lang/rust-clippy/pull/5912) +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import + [#5929](https://github.com/rust-lang/rust-clippy/pull/5929) +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable + [#5961](https://github.com/rust-lang/rust-clippy/pull/5961) +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut` + [#5933](https://github.com/rust-lang/rust-clippy/pull/5933) + +### False Positive Fixes + +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`] + [#5994](https://github.com/rust-lang/rust-clippy/pull/5994) +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts + [#5999](https://github.com/rust-lang/rust-clippy/pull/5999) +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures + [#5920](https://github.com/rust-lang/rust-clippy/pull/5920) +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer + [#5949](https://github.com/rust-lang/rust-clippy/pull/5949) +* [`doc_markdown`]: allow using "GraphQL" without backticks + [#5996](https://github.com/rust-lang/rust-clippy/pull/5996) +* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self` + [#5971](https://github.com/rust-lang/rust-clippy/pull/5971) +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays + [#6034](https://github.com/rust-lang/rust-clippy/pull/6034) +* [`should_implement_trait`]: ignore methods with lifetime parameters + [#5725](https://github.com/rust-lang/rust-clippy/pull/5725) +* [`needless_return`]: avoid linting if a temporary borrows a local variable + [#5903](https://github.com/rust-lang/rust-clippy/pull/5903) +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types + [#6006](https://github.com/rust-lang/rust-clippy/pull/6006) +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`] + [#5919](https://github.com/rust-lang/rust-clippy/pull/5919) +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types + [#6046](https://github.com/rust-lang/rust-clippy/pull/6046) + +### Suggestion Fixes/Improvements + +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments + [#5946](https://github.com/rust-lang/rust-clippy/pull/5946) +* [`useless_conversion`]: show the type in the error message + [#6035](https://github.com/rust-lang/rust-clippy/pull/6035) +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message + [#5892](https://github.com/rust-lang/rust-clippy/pull/5892) +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous + [#6043](https://github.com/rust-lang/rust-clippy/pull/6043) +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion + [#5993](https://github.com/rust-lang/rust-clippy/pull/5993) +* [`collapsible_if`]: don't use expanded code in the suggestion + [#5992](https://github.com/rust-lang/rust-clippy/pull/5992) +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`] + [#6042](https://github.com/rust-lang/rust-clippy/pull/6042) +* [`unit_arg`]: improve the readability of the suggestion + [#5931](https://github.com/rust-lang/rust-clippy/pull/5931) +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message + [#5935](https://github.com/rust-lang/rust-clippy/pull/5935) +* Show line count and max lines in [`too_many_lines`] lint message + [#6009](https://github.com/rust-lang/rust-clippy/pull/6009) +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable + [#5900](https://github.com/rust-lang/rust-clippy/pull/5900) +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly + [#6024](https://github.com/rust-lang/rust-clippy/pull/6024) +* [`redundant_allocation`]: suggest replacing `Rc>` with `Rc` + [#5899](https://github.com/rust-lang/rust-clippy/pull/5899) +* Make lint messages adhere to rustc dev guide conventions + [#5893](https://github.com/rust-lang/rust-clippy/pull/5893) + +### ICE Fixes + +* Fix ICE in [`repeat_once`] + [#5948](https://github.com/rust-lang/rust-clippy/pull/5948) + +### Documentation Improvements + +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation + [#6019](https://github.com/rust-lang/rust-clippy/pull/6019) +* [`unnecessary_mut_passed`]: fix typo + [#5913](https://github.com/rust-lang/rust-clippy/pull/5913) +* Add example of false positive to [`ptr_arg`] docs. + [#5885](https://github.com/rust-lang/rust-clippy/pull/5885) +* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box` + [#6023](https://github.com/rust-lang/rust-clippy/pull/6023) + +### Thanks + +This release of Clippy was made possible by the voluntary work of: mikerite, taiki-e, matthiaskrgr, rail-rain, wiomoc, +giraffate, tmiasko, HaramanJohal, Ryan1729, euclio, montrivo, thomcc, alex-700, ebroto, bugadani, Koxiaet, HactarCE, +ThibsG, deg4uss3r, stchris, rschoon, chansuke, jrqc, JarredAllen and scottmcm. ## Rust 1.47 -Current beta, release 2020-10-08 +Current stable, released 2020-10-08 [c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) @@ -112,7 +236,7 @@ Current beta, release 2020-10-08 ## Rust 1.46 -Current stable, released 2020-08-27 +Released 2020-08-27 [7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) From 5554641fceb640116316201bf669c04e1f86fcc3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 5 Oct 2020 22:32:04 +0200 Subject: [PATCH 0705/1110] Fix rustup fallout --- clippy_lints/src/attrs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c8f153e7201..f6eadbdef0b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -137,17 +137,17 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// // Good (as inner attribute) - /// #![inline(always)] + /// #![allow(dead_code)] /// /// fn this_is_fine() { } /// /// // Bad - /// #[inline(always)] + /// #[allow(dead_code)] /// /// fn not_quite_good_code() { } /// /// // Good (as outer attribute) - /// #[inline(always)] + /// #[allow(dead_code)] /// fn this_is_fine_too() { } /// ``` pub EMPTY_LINE_AFTER_OUTER_ATTR, From 13781ae2f8e31861c19a2c957d465d11569ea9ea Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 6 Oct 2020 00:09:45 +0200 Subject: [PATCH 0706/1110] Remove "thanks" section --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6631e6d546..0bd13320dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,12 +126,6 @@ Current beta, release 2020-11-19 * [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box` [#6023](https://github.com/rust-lang/rust-clippy/pull/6023) -### Thanks - -This release of Clippy was made possible by the voluntary work of: mikerite, taiki-e, matthiaskrgr, rail-rain, wiomoc, -giraffate, tmiasko, HaramanJohal, Ryan1729, euclio, montrivo, thomcc, alex-700, ebroto, bugadani, Koxiaet, HactarCE, -ThibsG, deg4uss3r, stchris, rschoon, chansuke, jrqc, JarredAllen and scottmcm. - ## Rust 1.47 Current stable, released 2020-10-08 From 4b459596a96a7d6f4797d4f1444311d88df60009 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Oct 2020 08:18:11 +0200 Subject: [PATCH 0707/1110] clippy_dev: Replace lazy_static with SyncLazy --- clippy_dev/Cargo.toml | 1 - clippy_dev/src/lib.rs | 42 +++++++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index c861efc8afb..d745000eac7 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -9,7 +9,6 @@ bytecount = "0.6" clap = "2.33" itertools = "0.9" regex = "1" -lazy_static = "1.0" shell-escape = "0.1" walkdir = "2" diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 5baa31d5cde..567831354f5 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,11 +1,12 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(once_cell)] use itertools::Itertools; -use lazy_static::lazy_static; use regex::Regex; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; +use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::WalkDir; @@ -15,28 +16,31 @@ pub mod ra_setup; pub mod stderr_length_check; pub mod update_lints; -lazy_static! { - static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new( +static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { + Regex::new( r#"(?x) - declare_clippy_lint!\s*[\{(] - (?:\s+///.*)* - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - (?P[a-z_]+)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] - "# + declare_clippy_lint!\s*[\{(] + (?:\s+///.*)* + \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* + (?P[a-z_]+)\s*,\s* + "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] +"#, ) - .unwrap(); - static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new( + .unwrap() +}); + +static DEC_DEPRECATED_LINT_RE: SyncLazy = SyncLazy::new(|| { + Regex::new( r#"(?x) - declare_deprecated_lint!\s*[{(]\s* - (?:\s+///.*)* - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] - "# + declare_deprecated_lint!\s*[{(]\s* + (?:\s+///.*)* + \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* + "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] +"#, ) - .unwrap(); - static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap(); -} + .unwrap() +}); +static NL_ESCAPE_RE: SyncLazy = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap()); pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; From 9b4ceee5930e5a7118ac25a0f424a7f83d01980f Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Oct 2020 08:20:18 +0200 Subject: [PATCH 0708/1110] integration tests: Replace lazy_static with SyncLazy --- tests/cargo/mod.rs | 37 +++++++++++++++++-------------------- tests/compile-test.rs | 1 + tests/dogfood.rs | 7 +++---- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs index 3c385343f70..a8f3e3145f6 100644 --- a/tests/cargo/mod.rs +++ b/tests/cargo/mod.rs @@ -1,27 +1,24 @@ -use lazy_static::lazy_static; use std::env; +use std::lazy::SyncLazy; use std::path::PathBuf; -lazy_static! { - pub static ref CARGO_TARGET_DIR: PathBuf = { - match env::var_os("CARGO_TARGET_DIR") { - Some(v) => v.into(), - None => env::current_dir().unwrap().join("target"), +pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), +}); + +pub static TARGET_LIB: SyncLazy = SyncLazy::new(|| { + if let Some(path) = option_env!("TARGET_LIBS") { + path.into() + } else { + let mut dir = CARGO_TARGET_DIR.clone(); + if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { + dir.push(target); } - }; - pub static ref TARGET_LIB: PathBuf = { - if let Some(path) = option_env!("TARGET_LIBS") { - path.into() - } else { - let mut dir = CARGO_TARGET_DIR.clone(); - if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { - dir.push(target); - } - dir.push(env!("PROFILE")); - dir - } - }; -} + dir.push(env!("PROFILE")); + dir + } +}); #[must_use] pub fn is_rustc_test_suite() -> bool { diff --git a/tests/compile-test.rs b/tests/compile-test.rs index f0d73e9b0e2..0e8f7683103 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,5 @@ #![feature(test)] // compiletest_rs requires this attribute +#![feature(once_cell)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 81af3d3033b..48e0478f169 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,15 +1,14 @@ // Dogfood cannot run on Windows #![cfg(not(windows))] +#![feature(once_cell)] -use lazy_static::lazy_static; +use std::lazy::SyncLazy; use std::path::PathBuf; use std::process::Command; mod cargo; -lazy_static! { - static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy"); -} +static CLIPPY_PATH: SyncLazy = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy")); #[test] fn dogfood_clippy() { From da57a16872297de5b6bb7754f722381631d4dca4 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Oct 2020 08:26:16 +0200 Subject: [PATCH 0709/1110] clippy_lints: Replace lazy_static with SyncLazy --- clippy_lints/Cargo.toml | 1 - clippy_lints/src/lib.rs | 1 + clippy_lints/src/macro_use.rs | 4 ++-- clippy_lints/src/utils/conf.rs | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 341d9e601ee..fcf817b82c8 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -20,7 +20,6 @@ edition = "2018" cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" -lazy_static = "1.0.2" pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ad8d12e6d..826a059f92a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -7,6 +7,7 @@ #![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(once_cell)] #![feature(or_patterns)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 065c7c042d3..b4b4b3dc18d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -18,9 +18,9 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust + /// ```rust,ignore /// #[macro_use] - /// use lazy_static; + /// use some_macro; /// ``` pub MACRO_USE_IMPORTS, pedantic, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 03f8c5a2c07..dd2fd0bb445 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -2,10 +2,10 @@ #![deny(clippy::missing_docs_in_private_items)] -use lazy_static::lazy_static; use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem}; use rustc_span::source_map; use source_map::Span; +use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::{env, fmt, fs, io}; @@ -54,9 +54,8 @@ impl From for Error { } } -lazy_static! { - static ref ERRORS: Mutex> = Mutex::new(Vec::new()); -} +/// Vec of errors that might be collected during config toml parsing +static ERRORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); macro_rules! define_Conf { ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { @@ -82,6 +81,7 @@ macro_rules! define_Conf { use serde::Deserialize; pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { use super::super::{ERRORS, Error}; + Ok( <$Ty>::deserialize(deserializer).unwrap_or_else(|e| { ERRORS From 7021d70d2e57e081597ea34eb18226a71b665ecd Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 6 Oct 2020 23:58:32 +0800 Subject: [PATCH 0710/1110] Use more concrete explanation for methods Show some code rather than "a single method call". --- clippy_lints/src/methods/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index dadd0f8ebb7..a9650e531b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -400,8 +400,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call using `_.flat_map(_)` + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.flat_map(_)` /// /// **Known problems:** /// @@ -424,8 +424,8 @@ declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter(_).map(_)`, /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.filter_map(_)`. /// /// **Known problems:** Often requires a condition + Option/Iterator creation /// inside the closure. @@ -452,8 +452,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find_map(_)`. /// /// **Known problems:** None /// @@ -496,8 +496,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.find(_).map(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find_map(_)`. /// /// **Known problems:** Often requires a condition + Option/Iterator creation /// inside the closure. @@ -1276,8 +1276,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.as_deref()`. /// /// **Known problems:** None. /// From b05aeaa9bce36d69d9ee13d1b5f6a271b6e8c1e5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 6 Oct 2020 23:32:38 +0200 Subject: [PATCH 0711/1110] Run fmt --- clippy_lints/src/future_not_send.rs | 9 ++------- clippy_lints/src/methods/mod.rs | 4 +--- clippy_lints/src/utils/mod.rs | 3 +-- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index d2a322e1223..71a30d1c33d 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await( - db, - &obligation, - ); - if let Trait(trait_pred, _) = - obligation.predicate.skip_binders() - { + infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred, _) = obligation.predicate.skip_binders() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e0651f9ab5d..ec70b2f1e20 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1668,9 +1668,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let ty::Opaque(def_id, _) = *ret_ty.kind() { // one of the associated types must be Self for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - if let ty::PredicateAtom::Projection(projection_predicate) = - predicate.skip_binders() - { + if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self if contains_ty(projection_predicate.ty, self_ty) { return; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 247effde19b..b2a8e3f08d5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1287,8 +1287,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateAtom::Trait(trait_predicate, _) = predicate.skip_binders() { - if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() - { + if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { return true; } } From 6c3611bdef09170a4b889dc759f986490a528e42 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 7 Oct 2020 00:02:28 +0200 Subject: [PATCH 0712/1110] Reinstate test for forbid blanket restriction --- tests/ui/attrs.rs | 3 --- tests/ui/attrs.stderr | 25 +++-------------- tests/ui/blanket_clippy_restriction_lints.rs | 8 ++++++ .../blanket_clippy_restriction_lints.stderr | 27 +++++++++++++++++++ 4 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 tests/ui/blanket_clippy_restriction_lints.rs create mode 100644 tests/ui/blanket_clippy_restriction_lints.stderr diff --git a/tests/ui/attrs.rs b/tests/ui/attrs.rs index 32685038067..8df6e19421e 100644 --- a/tests/ui/attrs.rs +++ b/tests/ui/attrs.rs @@ -1,8 +1,5 @@ #![warn(clippy::inline_always, clippy::deprecated_semver)] #![allow(clippy::assertions_on_constants)] -// Test that the whole restriction group is not enabled -#![warn(clippy::restriction)] -#![deny(clippy::restriction)] #![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)] #[inline(always)] diff --git a/tests/ui/attrs.stderr b/tests/ui/attrs.stderr index 4324984dd60..df4e9e20b64 100644 --- a/tests/ui/attrs.stderr +++ b/tests/ui/attrs.stderr @@ -1,5 +1,5 @@ error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea - --> $DIR/attrs.rs:8:1 + --> $DIR/attrs.rs:5:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[inline(always)] = note: `-D clippy::inline-always` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:28:14 + --> $DIR/attrs.rs:25:14 | LL | #[deprecated(since = "forever")] | ^^^^^^^^^^^^^^^^^ @@ -15,27 +15,10 @@ LL | #[deprecated(since = "forever")] = note: `-D clippy::deprecated-semver` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:31:14 + --> $DIR/attrs.rs:28:14 | LL | #[deprecated(since = "1")] | ^^^^^^^^^^^ -error: restriction lints are not meant to be all enabled - --> $DIR/attrs.rs:4:9 - | -LL | #![warn(clippy::restriction)] - | ^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: try enabling only the lints you really need - -error: restriction lints are not meant to be all enabled - --> $DIR/attrs.rs:5:9 - | -LL | #![deny(clippy::restriction)] - | ^^^^^^^^^^^^^^^^^^^ - | - = help: try enabling only the lints you really need - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/blanket_clippy_restriction_lints.rs b/tests/ui/blanket_clippy_restriction_lints.rs new file mode 100644 index 00000000000..d055f17526b --- /dev/null +++ b/tests/ui/blanket_clippy_restriction_lints.rs @@ -0,0 +1,8 @@ +#![warn(clippy::blanket_clippy_restriction_lints)] + +//! Test that the whole restriction group is not enabled +#![warn(clippy::restriction)] +#![deny(clippy::restriction)] +#![forbid(clippy::restriction)] + +fn main() {} diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr new file mode 100644 index 00000000000..537557f8b0a --- /dev/null +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -0,0 +1,27 @@ +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:4:9 + | +LL | #![warn(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:5:9 + | +LL | #![deny(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:6:11 + | +LL | #![forbid(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: aborting due to 3 previous errors + From a5ef305cb5a10437963ecda82a833b6932dc70d1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 5 Oct 2020 12:19:35 -0700 Subject: [PATCH 0713/1110] Downgrade string_lit_as_bytes to nursery --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/strings.rs | 2 +- src/lintlist/mod.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ad8d12e6d..6ae8ae9e38b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1475,7 +1475,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), - LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1618,7 +1617,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&strings::STRING_LIT_AS_BYTES), 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), @@ -1831,6 +1829,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_borrow::NEEDLESS_BORROW), LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), + LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&transmute::USELESS_TRANSMUTE), LintId::of(&use_self::USE_SELF), ]); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 15b66684eab..203af06a734 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -80,7 +80,7 @@ declare_clippy_lint! { /// let bs = b"a byte string"; /// ``` pub STRING_LIT_AS_BYTES, - style, + nursery, "calling `as_bytes` on a string literal instead of using a byte string literal" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0dba5a71c50..959ac6d42bf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2161,7 +2161,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "string_lit_as_bytes", - group: "style", + group: "nursery", desc: "calling `as_bytes` on a string literal instead of using a byte string literal", deprecation: None, module: "strings", From 0e159a55d66f4c3cd56237f28a36f31599442102 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 6 Oct 2020 19:46:03 -0700 Subject: [PATCH 0714/1110] Downgrade rc_buffer to restriction --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/types.rs | 2 +- src/lintlist/mod.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ad8d12e6d..a5a24536772 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1171,6 +1171,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), @@ -1504,7 +1505,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::TYPE_COMPLEXITY), LintId::of(&types::UNIT_ARG), @@ -1805,7 +1805,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), - LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), ]); diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 17d950169fd..5e83b6c81ec 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -242,7 +242,7 @@ declare_clippy_lint! { /// fn foo(interned: Rc) { ... } /// ``` pub RC_BUFFER, - perf, + restriction, "shared ownership of a buffer type" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0dba5a71c50..cc66b1a2db6 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1888,7 +1888,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "rc_buffer", - group: "perf", + group: "restriction", desc: "shared ownership of a buffer type", deprecation: None, module: "types", From 5bad9175fb363917ffee2c2da7223e96daa2f5ac Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:37:32 +0200 Subject: [PATCH 0715/1110] New lint: Recommend using `ptr::eq` when possible This is based almost entirely on the code available in the previous PR #4596. --- clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/ptr_eq.rs | 96 ++++++++++++++++++++++++++++++++++++++ tests/ui/ptr_eq.fixed | 38 +++++++++++++++ tests/ui/ptr_eq.rs | 38 +++++++++++++++ tests/ui/ptr_eq.stderr | 16 +++++++ 5 files changed, 193 insertions(+) create mode 100644 clippy_lints/src/ptr_eq.rs create mode 100644 tests/ui/ptr_eq.fixed create mode 100644 tests/ui/ptr_eq.rs create mode 100644 tests/ui/ptr_eq.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 826a059f92a..d11eeff8032 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -281,6 +281,7 @@ mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; mod ptr; +mod ptr_eq; mod ptr_offset_with_cast; mod question_mark; mod ranges; @@ -778,6 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ptr::CMP_NULL, &ptr::MUT_FROM_REF, &ptr::PTR_ARG, + &ptr_eq::PTR_EQ, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &question_mark::QUESTION_MARK, &ranges::RANGE_MINUS_ONE, @@ -916,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold)); store.register_late_pass(|| box ptr::Ptr); + store.register_late_pass(|| box ptr_eq::PtrEq); store.register_late_pass(|| box needless_bool::NeedlessBool); store.register_late_pass(|| box needless_bool::BoolComparison); store.register_late_pass(|| box approx_const::ApproxConstant); @@ -1456,6 +1459,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), @@ -1612,6 +1616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs new file mode 100644 index 00000000000..a05cb6270b7 --- /dev/null +++ b/clippy_lints/src/ptr_eq.rs @@ -0,0 +1,96 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Use `std::ptr::eq` when applicable + /// + /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(PtrEq => [PTR_EQ]); + +static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; + +impl LateLintPass<'_> for PtrEq { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if utils::in_macro(expr.span) { + return; + } + + if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if BinOpKind::Eq == op.node { + let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs), + _ => (&**left, &**right), + }; + + if_chain! { + if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); + if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); + if let Some(left_snip) = utils::snippet_opt(cx, left_var.span); + if let Some(right_snip) = utils::snippet_opt(cx, right_var.span); + then { + utils::span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + LINT_MSG, + "try", + format!("std::ptr::eq({}, {})", left_snip, right_snip), + Applicability::MachineApplicable, + ); + } + } + } + } + } +} + +// If the given expression is a cast to an usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} + +// If the given expression is a cast to a `*const` pointer, return the lhs of the cast +// E.g., `foo as *const _` returns `foo`. +fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed new file mode 100644 index 00000000000..209081e6e80 --- /dev/null +++ b/tests/ui/ptr_eq.fixed @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = std::ptr::eq(a, b); + let _ = std::ptr::eq(a, b); + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs new file mode 100644 index 00000000000..69162870807 --- /dev/null +++ b/tests/ui/ptr_eq.rs @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = a as *const _ as usize == b as *const _ as usize; + let _ = a as *const _ == b as *const _; + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr new file mode 100644 index 00000000000..45d8c60382b --- /dev/null +++ b/tests/ui/ptr_eq.stderr @@ -0,0 +1,16 @@ +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:20:13 + | +LL | let _ = a as *const _ as usize == b as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + | + = note: `-D clippy::ptr-eq` implied by `-D warnings` + +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:21:13 + | +LL | let _ = a as *const _ == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + +error: aborting due to 2 previous errors + From aa7c42f75668bc514baf0a64f31771353c3af6cb Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:54:00 +0200 Subject: [PATCH 0716/1110] fixup! New lint: Recommend using `ptr::eq` when possible Add missing modified files. --- CHANGELOG.md | 1 + src/lintlist/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 617bf32f463..552a0e96ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1774,6 +1774,7 @@ Released 2018-09-13 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0dba5a71c50..3cba9867600 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1844,6 +1844,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "ptr", }, + Lint { + name: "ptr_eq", + group: "style", + desc: "use `std::ptr::eq` when comparing raw pointers", + deprecation: None, + module: "ptr_eq", + }, Lint { name: "ptr_offset_with_cast", group: "complexity", From 9ef311b47706cc0babce08ee86bc9ca7727fc2f5 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:59:20 +0200 Subject: [PATCH 0717/1110] Rename tables to typecheck_result() --- doc/common_tools_writing_lints.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 53c3d084dbc..27d4f1b03d7 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -45,11 +45,13 @@ Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`] to retrieve a type from a pattern. Two noticeable items here: -- `cx` is the lint context [`LateContext`][LateContext]. - The two most useful data structures in this context are `tcx` and `tables`, - allowing us to jump to type definitions and other compilation stages such as HIR. -- `tables` is [`TypeckResults`][TypeckResults] and is created by type checking step, - it includes useful information such as types of expressions, ways to resolve methods and so on. +- `cx` is the lint context [`LateContext`][LateContext]. The two most useful + data structures in this context are `tcx` and the `TypeckResults` returned by + 'LateContext::typeck_results', allowing us to jump to type definitions and + other compilation stages such as HIR. +- `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is + created by type checking step, it includes useful information such as types + of expressions, ways to resolve methods and so on. # Checking if an expr is calling a specific method From 326090dbb8285e9486d77fd9aa2ea206f78bf978 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 17:10:31 +0200 Subject: [PATCH 0718/1110] fixup! Rename tables to typecheck_result() Co-authored-by: Takayuki Nakata --- doc/common_tools_writing_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 27d4f1b03d7..d56079a4ab7 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -47,7 +47,7 @@ to retrieve a type from a pattern. Two noticeable items here: - `cx` is the lint context [`LateContext`][LateContext]. The two most useful data structures in this context are `tcx` and the `TypeckResults` returned by - 'LateContext::typeck_results', allowing us to jump to type definitions and + `LateContext::typeck_results`, allowing us to jump to type definitions and other compilation stages such as HIR. - `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is created by type checking step, it includes useful information such as types From 6edde811b5437f926c26b1b844acd60e3dd0a0c9 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 17:11:11 +0200 Subject: [PATCH 0719/1110] fixup! New lint: Recommend using `ptr::eq` when possible Co-authored-by: Takayuki Nakata --- clippy_lints/src/ptr_eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index a05cb6270b7..3be792ce5e4 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Use `std::ptr::eq` when applicable /// - /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// **Why is this bad?** `ptr::eq` can be used to compare `&T` references /// (which coerce to `*const T` implicitly) by their address rather than /// comparing the values they point to. /// From 11672577debb4880edb5641c470c78175b121bcb Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 8 Oct 2020 01:07:00 +0200 Subject: [PATCH 0720/1110] Fix unicode regexen with bytes::Regex fixes #6005 --- clippy_lints/src/regex.rs | 2 +- tests/ui/regex.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index dfc158661cb..95594e38c9e 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -143,7 +143,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { let mut parser = regex_syntax::ParserBuilder::new() - .unicode(utf8) + .unicode(true) .allow_invalid_utf8(!utf8) .build(); diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 9767e5bf76a..f7f3b195ccc 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -71,6 +71,9 @@ fn trivial_regex() { let non_trivial_ends_with = Regex::new("foo|bar"); let non_trivial_binary = BRegex::new("foo|bar"); let non_trivial_binary_builder = BRegexBuilder::new("foo|bar"); + + // #6005: unicode classes in bytes::Regex + let a_byte_of_unicode = BRegex::new(r"\p{C}"); } fn main() { From 738ed383066a561ae4f3ef0a6a2d570eaba7b6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Wed, 7 Oct 2020 21:41:54 -0300 Subject: [PATCH 0721/1110] clippy_lints: Do not warn against Box parameter in C FFI Fixes #5542. When using C FFI, to handle pointers in parameters it is needed to declare them as `Box` in its Rust-side signature. However, the current linter warns against the usage of Box stating that "local variable doesn't need to be boxed here". This commit fixes it by ignoring functions whose Abi is Cdecl. --- clippy_lints/src/escape.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 82549c12d0a..ffe81b3966c 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -6,6 +6,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use crate::utils::span_lint; @@ -60,12 +61,18 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: intravisit::FnKind<'tcx>, + fn_kind: intravisit::FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, _: Span, hir_id: HirId, ) { + if let Some(header) = fn_kind.header() { + if header.abi == Abi::Cdecl { + return; + } + } + // If the method is an impl for a trait, don't warn. let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_node = cx.tcx.hir().find(parent_id); From 15150c07eaeec50138a9c96c5bdecbdf92c3101e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Wed, 7 Oct 2020 22:49:50 -0300 Subject: [PATCH 0722/1110] clippy_lint: Test for BoxedLocal false-positive in C-FFI and fix C-FFI Abi comparison. --- clippy_lints/src/escape.rs | 2 +- tests/ui/escape_analysis.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index ffe81b3966c..b222475486d 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { hir_id: HirId, ) { if let Some(header) = fn_kind.header() { - if header.abi == Abi::Cdecl { + if header.abi == Abi::C { return; } } diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index c0a52d832c0..377a0fb502f 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -174,3 +174,8 @@ mod issue_3739 { }; } } + +/// Issue #5542 +/// +/// This shouldn't warn for `boxed_local` as it is a function implemented in C. +pub extern "C" fn do_now_warn_me(_c_pointer: Box) -> () {} From 3a6f59ecae90892c3162df95d9633c4263ab91fa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 8 Oct 2020 04:50:24 -0700 Subject: [PATCH 0723/1110] Document string_lit_as_bytes known problems --- clippy_lints/src/strings.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 203af06a734..415c07b2261 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -69,7 +69,27 @@ declare_clippy_lint! { /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used /// instead. They are shorter but less discoverable than `as_bytes()`. /// - /// **Known Problems:** None. + /// **Known Problems:** + /// `"str".as_bytes()` and the suggested replacement of `b"str"` are not + /// equivalent because they have different types. The former is `&[u8]` + /// while the latter is `&[u8; 3]`. That means in general they will have a + /// different set of methods and different trait implementations. + /// + /// ```rust + /// fn f(v: Vec) {} + /// + /// f("...".as_bytes().to_owned()); // works + /// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec + /// + /// fn g(r: impl std::io::Read) {} + /// + /// g("...".as_bytes()); // works + /// g(b"..."); // does not work + /// ``` + /// + /// The actual equivalent of `"str".as_bytes()` with the same type is not + /// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not + /// more readable than a function call. /// /// **Example:** /// ```rust From c81bea45f480d9d5a785778c944a3ad3daf30d27 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 8 Oct 2020 04:54:17 -0700 Subject: [PATCH 0724/1110] Make clippy_lints's doc tests succeed --- clippy_lints/src/strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 415c07b2261..3783bd78de2 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -75,7 +75,7 @@ declare_clippy_lint! { /// while the latter is `&[u8; 3]`. That means in general they will have a /// different set of methods and different trait implementations. /// - /// ```rust + /// ```compile_fail /// fn f(v: Vec) {} /// /// f("...".as_bytes().to_owned()); // works From cc26924cce6d6e6ee7913ba5026d205eefd42702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Thu, 8 Oct 2020 09:03:11 -0300 Subject: [PATCH 0725/1110] clippy_lint: Extend BoxedLocal ignored ABI to all non-rust ABIs. --- clippy_lints/src/escape.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index b222475486d..8b022912573 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { hir_id: HirId, ) { if let Some(header) = fn_kind.header() { - if header.abi == Abi::C { + if header.abi != Abi::Rust { return; } } From 418cde0389d47628b32e533c47e64874fd1050fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Thu, 8 Oct 2020 09:06:19 -0300 Subject: [PATCH 0726/1110] clippy_lint: Fix typo (now -> not) --- tests/ui/escape_analysis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 377a0fb502f..3d5a02bf705 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -177,5 +177,5 @@ mod issue_3739 { /// Issue #5542 /// -/// This shouldn't warn for `boxed_local` as it is a function implemented in C. -pub extern "C" fn do_now_warn_me(_c_pointer: Box) -> () {} +/// This shouldn't warn for `boxed_local` as it is a function to be used in C. +pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} From 5ae0f2644d4c402c8ea3561de1c9fa829b11b40e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Thu, 8 Oct 2020 09:07:24 -0300 Subject: [PATCH 0727/1110] clippy_lint: extern definition is in Rust, not C --- tests/ui/escape_analysis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 3d5a02bf705..7d3f4011e99 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -177,5 +177,5 @@ mod issue_3739 { /// Issue #5542 /// -/// This shouldn't warn for `boxed_local` as it is a function to be used in C. +/// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} From e6a71066c8e9c0aaaabd10923f73c81285b16932 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 8 Oct 2020 05:18:22 -0700 Subject: [PATCH 0728/1110] Clippy dev subcommand to build and serve website --- clippy_dev/Cargo.toml | 1 + clippy_dev/src/lib.rs | 1 + clippy_dev/src/main.rs | 20 ++++++++++++- clippy_dev/src/serve.rs | 64 +++++++++++++++++++++++++++++++++++++++++ doc/adding_lints.md | 3 +- 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 clippy_dev/src/serve.rs diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index d745000eac7..b8a4a20114b 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" bytecount = "0.6" clap = "2.33" itertools = "0.9" +opener = "0.4" regex = "1" shell-escape = "0.1" walkdir = "2" diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 567831354f5..43cb2954b74 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -13,6 +13,7 @@ use walkdir::WalkDir; pub mod fmt; pub mod new_lint; pub mod ra_setup; +pub mod serve; pub mod stderr_length_check; pub mod update_lints; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 281037ae37c..7a8cbd5251d 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints}; +use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; fn main() { let matches = App::new("Clippy developer tooling") @@ -100,6 +100,19 @@ fn main() { .required(true), ), ) + .subcommand( + SubCommand::with_name("serve") + .about("Launch a local 'ALL the Clippy Lints' website in a browser") + .arg( + Arg::with_name("port") + .long("port") + .short("p") + .help("Local port for the http server") + .default_value("8000") + .validator_os(serve::validate_port), + ) + .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), + ) .get_matches(); match matches.subcommand() { @@ -129,6 +142,11 @@ fn main() { stderr_length_check::check(); }, ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + ("serve", Some(matches)) => { + let port = matches.value_of("port").unwrap().parse().unwrap(); + let lint = matches.value_of("lint"); + serve::run(port, lint); + }, _ => {}, } } diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs new file mode 100644 index 00000000000..a46c0e4d3f0 --- /dev/null +++ b/clippy_dev/src/serve.rs @@ -0,0 +1,64 @@ +use std::ffi::{OsStr, OsString}; +use std::path::Path; +use std::process::Command; +use std::thread; +use std::time::{Duration, SystemTime}; + +pub fn run(port: u16, lint: Option<&str>) -> ! { + let mut url = Some(match lint { + None => format!("http://localhost:{}", port), + Some(lint) => format!("http://localhost:{}/#{}", port, lint), + }); + + loop { + if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") { + Command::new("python3") + .arg("util/export.py") + .spawn() + .unwrap() + .wait() + .unwrap(); + } + if let Some(url) = url.take() { + thread::spawn(move || { + Command::new("python3") + .arg("-m") + .arg("http.server") + .arg(port.to_string()) + .current_dir("util/gh-pages") + .spawn() + .unwrap(); + // Give some time for python to start + thread::sleep(Duration::from_millis(500)); + // Launch browser after first export.py has completed and http.server is up + let _ = opener::open(url); + }); + } + thread::sleep(Duration::from_millis(1000)); + } +} + +fn mtime(path: impl AsRef) -> SystemTime { + let path = path.as_ref(); + if path.is_dir() { + path.read_dir() + .into_iter() + .flatten() + .flatten() + .map(|entry| mtime(&entry.path())) + .max() + .unwrap_or(SystemTime::UNIX_EPOCH) + } else { + path.metadata() + .and_then(|metadata| metadata.modified()) + .unwrap_or(SystemTime::UNIX_EPOCH) + } +} + +#[allow(clippy::missing_errors_doc)] +pub fn validate_port(arg: &OsStr) -> Result<(), OsString> { + match arg.to_string_lossy().parse::() { + Ok(_port) => Ok(()), + Err(err) => Err(OsString::from(err.to_string())), + } +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 21e0f6f4fc7..2869c3bf7d4 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -189,7 +189,8 @@ declare_clippy_lint! { * The section of lines prefixed with `///` constitutes the lint documentation section. This is the default documentation style and will be displayed - [like this][example_lint_page]. + [like this][example_lint_page]. To render and open this documentation locally + in a browser, run `cargo dev serve`. * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming guidelines][lint_naming] here when naming your lint. In short, the name should state the thing that is being checked for and From fd61686ad58015f9307c72b632ba19bf4c27a219 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 8 Oct 2020 23:13:06 +0900 Subject: [PATCH 0729/1110] Add note that we follow a rustc no merge-commit policy --- doc/basics.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/basics.md b/doc/basics.md index c81e7f6e069..38959e2331b 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -13,6 +13,7 @@ Lints] or [Common Tools]. - [Setup](#setup) - [Building and Testing](#building-and-testing) - [`cargo dev`](#cargo-dev) + - [PR](#pr) ## Get the Code @@ -110,3 +111,8 @@ cargo dev new_lint # (experimental) Setup Clippy to work with rust-analyzer cargo dev ra-setup ``` + +## PR + +We follow a rustc no merge-commit policy. +See . From b709b873632dc001b381704c1deb6be702111706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Thu, 8 Oct 2020 18:50:52 -0300 Subject: [PATCH 0730/1110] tests: Add test function that does not specify ABI --- tests/ui/escape_analysis.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 7d3f4011e99..07004489610 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -179,3 +179,6 @@ mod issue_3739 { /// /// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} + +#[rustfmt::skip] // Forces rustfmt to not add ABI +pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} From 2c8cd7d93cd137d9f606d15c20b92634c68c98f4 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 09:17:48 +0200 Subject: [PATCH 0731/1110] Update actions due to deprecation of set-env and add-path --- .github/workflows/clippy.yml | 4 ++-- .github/workflows/clippy_bors.yml | 14 +++++++------- .github/workflows/clippy_dev.yml | 4 ++-- .github/workflows/deploy.yml | 4 ++-- .github/workflows/remark.yml | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 99e371631b1..0cbe73affda 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -36,14 +36,14 @@ jobs: github_token: "${{ secrets.github_token }}" - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index fd0cd7a1890..a5c00b3418e 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -20,7 +20,7 @@ jobs: with: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 with: ref: ${{ github.ref }} @@ -81,14 +81,14 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: ${{ matrix.host }} profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update @@ -177,14 +177,14 @@ jobs: github_token: "${{ secrets.github_token }}" - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update @@ -258,14 +258,14 @@ jobs: github_token: "${{ secrets.github_token }}" - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index ec3b43c2f43..5ee157cf23b 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu @@ -31,7 +31,7 @@ jobs: components: rustfmt - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f542f9b02c1..5b7252532c2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index cc175e8bf24..4f25a86b2e4 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,10 +16,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Setup Node.js - uses: actions/setup-node@v1.1.0 + uses: actions/setup-node@v1.4.4 - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended From c85cb76064ae24ee16efa426280ccbd7b7b54f4b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 09:40:57 +0200 Subject: [PATCH 0732/1110] Update release documentation --- doc/release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release.md b/doc/release.md index 391952ea6b1..eaa6a9af277 100644 --- a/doc/release.md +++ b/doc/release.md @@ -68,7 +68,7 @@ be updated. ```bash # Assuming the current directory corresponds to the Clippy repository $ git checkout beta -$ git rebase $BETA_SHA +$ git reset --hard $BETA_SHA $ git push upstream beta ``` From 732d370404198ca68a96901b25a5f2ab3ba9d562 Mon Sep 17 00:00:00 2001 From: Jean SIMARD Date: Fri, 9 Oct 2020 09:47:53 +0200 Subject: [PATCH 0733/1110] clippy_lint: Fix doc on 'option_if_let_else' - a typo in `expresion` - a useless double space - Some back-tick quotes for 'if let' --- clippy_lints/src/option_if_let_else.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4a3eb9c983a..383a62da821 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -13,14 +13,14 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more /// idiomatically done with `Option::map_or` (if the else bit is a pure /// expression) or `Option::map_or_else` (if the else bit is an impure - /// expresion). + /// expression). /// /// **Why is this bad?** /// Using the dedicated functions of the Option type is clearer and - /// more concise than an if let expression. + /// more concise than an `if let` expression. /// /// **Known problems:** /// This lint uses a deliberately conservative metric for checking From 64a7d019f10bafa386817de3bb15ef2eead4cda5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 10:40:04 +0200 Subject: [PATCH 0734/1110] Remove all usage of set-env --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 8 ++++---- .github/workflows/deploy.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 0cbe73affda..cf4aa39e49b 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -63,7 +63,7 @@ jobs: - name: Set LD_LIBRARY_PATH (Linux) run: | SYSROOT=$(rustc --print sysroot) - echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" + echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build run: cargo build --features deny-warnings diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index a5c00b3418e..f83861950f8 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -112,7 +112,7 @@ jobs: if: runner.os == 'Linux' run: | SYSROOT=$(rustc --print sysroot) - echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" + echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Link rustc dylib (MacOS) if: runner.os == 'macOS' run: | @@ -122,9 +122,9 @@ jobs: - name: Set PATH (Windows) if: runner.os == 'Windows' run: | - $sysroot = rustc --print sysroot - $env:PATH += ';' + $sysroot + '\bin' - echo "::set-env name=PATH::$env:PATH" + SYSROOT=$(rustc --print sysroot) + echo "$SYSROOT/bin" >> $GITHUB_PATH + shell: bash - name: Build run: cargo build --features deny-warnings diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5b7252532c2..15aeaf907dc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -34,10 +34,10 @@ jobs: if: startswith(github.ref, 'refs/tags/') run: | TAG=$(basename ${{ github.ref }}) - echo "::set-env name=TAG_NAME::$TAG" + echo "TAG_NAME=$TAG" >> $GITHUB_ENV - name: Set beta to true if: github.ref == 'refs/heads/beta' - run: echo "::set-env name=BETA::true" + run: echo "BETA=true" >> $GITHUB_ENV - name: Use scripts and templates from master branch run: | From c4c9453ccfc69d00ced80db4cdd5be805f1ee1c2 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 11:53:10 +0200 Subject: [PATCH 0735/1110] Use defaults.run.shell instead of setting shell every time --- .github/workflows/clippy_bors.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f83861950f8..7509d90c6c2 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -11,6 +11,10 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 +defaults: + run: + shell: bash + jobs: changelog: runs-on: ubuntu-latest @@ -105,7 +109,6 @@ jobs: run: bash setup-toolchain.sh env: HOST_TOOLCHAIN: ${{ matrix.host }} - shell: bash # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -124,39 +127,31 @@ jobs: run: | SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - shell: bash - name: Build run: cargo build --features deny-warnings - shell: bash - name: Test run: cargo test --features deny-warnings - shell: bash - name: Test clippy_lints run: cargo test --features deny-warnings - shell: bash working-directory: clippy_lints - name: Test rustc_tools_util run: cargo test --features deny-warnings - shell: bash working-directory: rustc_tools_util - name: Test clippy_dev run: cargo test --features deny-warnings - shell: bash working-directory: clippy_dev - name: Test cargo-clippy run: ../target/debug/cargo-clippy - shell: bash working-directory: clippy_workspace_tests - name: Test clippy-driver run: bash .github/driver.sh - shell: bash env: OS: ${{ runner.os }} @@ -165,7 +160,7 @@ jobs: run: | cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache cargo cache - shell: bash + integration_build: needs: changelog runs-on: ubuntu-latest From 7f846c930b9d35061e6eb6b8dbfbc890d38d539c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 14:14:49 +0200 Subject: [PATCH 0736/1110] Update backport documentation to the subtree workflow --- doc/backport.md | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/doc/backport.md b/doc/backport.md index 259696658ea..15f3d1f0806 100644 --- a/doc/backport.md +++ b/doc/backport.md @@ -5,7 +5,7 @@ Backports in Clippy are rare and should be approved by the Clippy team. For example, a backport is done, if a crucial ICE was fixed or a lint is broken to a point, that it has to be disabled, before landing on stable. -Backports are done to the `beta` release of Clippy. Backports to stable Clippy +Backports are done to the `beta` branch of Clippy. Backports to stable Clippy releases basically don't exist, since this would require a Rust point release, which is almost never justifiable for a Clippy fix. @@ -18,7 +18,31 @@ Backports are done on the beta branch of the Clippy repository. # Assuming the current directory corresponds to the Clippy repository $ git checkout beta $ git checkout -b backport -$ git cherry-pick # `` is the commit hash of the commit, that should be backported +$ git cherry-pick # `` is the commit hash of the commit(s), that should be backported +$ git push origin backport +``` + +Now you should test that the backport passes all the tests in the Rust +repository. You can do this with: + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git checkout beta +$ git subtree pull -p src/tools/clippy https://github.com//rust-clippy backport +$ ./x.py test src/tools/clippy +``` + +Should the test fail, you can fix Clippy directly in the Rust repository. This +has to be first applied to the Clippy beta branch and then again synced to the +Rust repository, though. The easiest way to do this is: + +```bash +# In the Rust repository +$ git diff --patch --relative=src/tools/clippy > clippy.patch +# In the Clippy repository +$ git apply /path/to/clippy.patch +$ git add -u +$ git commit -m "Fix rustup fallout" $ git push origin backport ``` @@ -29,22 +53,19 @@ After this, you can open a PR to the `beta` branch of the Clippy repository. This step must be done, **after** the PR of the previous step was merged. -After the backport landed in the Clippy repository, also the Clippy version on -the Rust `beta` branch has to be updated. +After the backport landed in the Clippy repository, the branch has to be synced +back to the beta branch of the Rust repository. ```bash # Assuming the current directory corresponds to the Rust repository $ git checkout beta $ git checkout -b clippy_backport -$ pushd src/tools/clippy -$ git fetch -$ git checkout beta -$ popd -$ git add src/tools/clippy -§ git commit -m "Update Clippy" +$ git subtree pull -p src/tools/clippy https://github.com/rust-lang/rust-clippy beta $ git push origin clippy_backport ``` -After this you can open a PR to the `beta` branch of the Rust repository. In -this PR you should tag the Clippy team member, that agreed to the backport or -the `@rust-lang/clippy` team. Make sure to add `[beta]` to the title of the PR. +Make sure to test the backport in the Rust repository before opening a PR. This +is done with `./x.py test src/tools/clippy`. If that passes all tests, open a PR +to the `beta` branch of the Rust repository. In this PR you should tag the +Clippy team member, that agreed to the backport or the `@rust-lang/clippy` team. +Make sure to add `[beta]` to the title of the PR. From 7b7ddfa55da889b41e90243ac59a04eed832a71e Mon Sep 17 00:00:00 2001 From: Sebastian Andersson Date: Fri, 9 Oct 2020 20:23:03 +0200 Subject: [PATCH 0737/1110] Preserve raw strs for: format!(s) to s.to_string() lint Ie: | let s = format!(r#""hello""#); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `r#""hello""#.to_string()` --- clippy_lints/src/format.rs | 8 ++++++-- tests/ui/format.fixed | 3 ++- tests/ui/format.stderr | 8 +++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index d6541010bca..26da058598e 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, + is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, span_lint_and_then, }; use if_chain::if_chain; @@ -132,7 +132,11 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option [], }` if tup.is_empty() { - return Some(format!("{:?}.to_string()", s.as_str())); + if let Some(s_src) = snippet_opt(cx, lit.span) { + // Simulate macro expansion, converting {{ and }} to { and }. + let s_expand = s_src.replace("{{", "{").replace("}}", "}"); + return Some(format!("{}.to_string()", s_expand)) + } } else if s.as_str().is_empty() { return on_argumentv1_new(cx, &tup[0], arms); } diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 30651476999..740a22a07d7 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -13,7 +13,8 @@ fn main() { "foo".to_string(); "{}".to_string(); "{} abc {}".to_string(); - "foo {}\n\" bar".to_string(); + r##"foo {} +" bar"##.to_string(); "foo".to_string(); format!("{:?}", "foo"); // Don't warn about `Debug`. diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 9734492154e..96df7f37f77 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -25,7 +25,13 @@ LL | / format!( LL | | r##"foo {{}} LL | | " bar"## LL | | ); - | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();` + | |______^ + | +help: consider using `.to_string()` + | +LL | r##"foo {} +LL | " bar"##.to_string(); + | error: useless use of `format!` --> $DIR/format.rs:21:5 From 26e4de9557e5924d001652138ec0f8f40dc40372 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 9 Oct 2020 00:49:27 +0200 Subject: [PATCH 0738/1110] allow refs in our constant handling --- clippy_lints/src/consts.rs | 10 +++++++++- tests/ui/float_cmp.rs | 5 +++++ tests/ui/float_cmp.stderr | 12 ++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 062c9bd2d9e..c5e33b288a9 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -40,6 +40,8 @@ pub enum Constant { Tuple(Vec), /// A raw pointer. RawPtr(u128), + /// A reference + Ref(Box), /// A literal with syntax error. Err(Symbol), } @@ -66,6 +68,7 @@ impl PartialEq for Constant { (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -110,6 +113,9 @@ impl Hash for Constant { Self::RawPtr(u) => { u.hash(state); }, + Self::Ref(ref r) => { + r.hash(state); + }, Self::Err(ref s) => { s.hash(state); }, @@ -144,6 +150,7 @@ impl Constant { x => x, } }, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), // TODO: are there any useful inter-type orderings? _ => None, } @@ -239,7 +246,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { UnOp::UnNot => self.constant_not(&o, self.typeck_results.expr_ty(e)), UnOp::UnNeg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), - UnOp::UnDeref => Some(o), + UnOp::UnDeref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), ExprKind::Call(ref callee, ref args) => { @@ -269,6 +276,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } }, ExprKind::Index(ref arr, ref index) => self.index(arr, index), + ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), // TODO: add other expressions. _ => None, } diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 9fa0e5f5c07..586784b73e6 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -2,6 +2,7 @@ #![allow( unused, clippy::no_effect, + clippy::op_ref, clippy::unnecessary_operation, clippy::cast_lossless, clippy::many_single_char_names @@ -116,4 +117,8 @@ fn main() { 1.23f64.signum() != x64.signum(); 1.23f64.signum() != -(x64.signum()); 1.23f64.signum() != 3.21f64.signum(); + + // the comparison should also look through references + &0.0 == &ZERO; + &&&&0.0 == &&&&ZERO; } diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index f7c380fc915..bb4051c4662 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:65:5 + --> $DIR/float_cmp.rs:66:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:70:5 + --> $DIR/float_cmp.rs:71:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -16,7 +16,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:73:5 + --> $DIR/float_cmp.rs:74:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:93:5 + --> $DIR/float_cmp.rs:94:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:99:5 | LL | a1 == a2; | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:99:5 + --> $DIR/float_cmp.rs:100:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` From 6d88803a1c5516fda1d1030e5676d6b15be130fc Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 22:21:47 +0200 Subject: [PATCH 0739/1110] Add regression test for ICE 6139 --- tests/ui/crashes/ice-6139.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/ui/crashes/ice-6139.rs diff --git a/tests/ui/crashes/ice-6139.rs b/tests/ui/crashes/ice-6139.rs new file mode 100644 index 00000000000..f3966e47f5e --- /dev/null +++ b/tests/ui/crashes/ice-6139.rs @@ -0,0 +1,7 @@ +trait T<'a> {} + +fn foo(_: Vec>>) {} + +fn main() { + foo(vec![]); +} From a98f9d21fcdad85ae95d00d3931cf438f9d5a9de Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 22:22:21 +0200 Subject: [PATCH 0740/1110] (Hacky) Fix for ICE #6139 --- clippy_lints/src/types.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5e83b6c81ec..a982b92bb2b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -541,6 +541,11 @@ impl Types { _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); + // HACK(flip1995): This is a fix for an ICE occuring when `ty_ty` is a + // trait object with a lifetime, e.g. `dyn T<'_>`. Since trait objects + // don't have a known size, this shouldn't introduce FNs. But there + // should be a better solution. + if !matches!(ty_ty.kind(), ty::Dynamic(..)); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size <= self.vec_box_size_threshold; From cf81975d7765bf0cf82fee9f3e991c880f77cd13 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 10 Oct 2020 14:04:14 +0200 Subject: [PATCH 0741/1110] Fix two ICEs caused by ty.is_{sized,freeze} --- clippy_lints/src/mut_key.rs | 7 ++++++- clippy_lints/src/types.rs | 7 ++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 8a2dbdc50ea..4525b12689f 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,6 +1,7 @@ use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -120,7 +121,11 @@ fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bo size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span) }, Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)), - Adt(..) => cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(span), cx.param_env), + Adt(..) => { + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.has_escaping_bound_vars() + && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + }, _ => false, } } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a982b92bb2b..9a948af8bfc 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -17,6 +17,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -541,11 +542,7 @@ impl Types { _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); - // HACK(flip1995): This is a fix for an ICE occuring when `ty_ty` is a - // trait object with a lifetime, e.g. `dyn T<'_>`. Since trait objects - // don't have a known size, this shouldn't introduce FNs. But there - // should be a better solution. - if !matches!(ty_ty.kind(), ty::Dynamic(..)); + if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size <= self.vec_box_size_threshold; From 52e650ae88a63b41686f646f2240de7c870e6ea6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 10 Oct 2020 15:03:49 +0200 Subject: [PATCH 0742/1110] Add test for ICE #6153 --- tests/ui/crashes/ice-6153.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/ui/crashes/ice-6153.rs diff --git a/tests/ui/crashes/ice-6153.rs b/tests/ui/crashes/ice-6153.rs new file mode 100644 index 00000000000..9f73f39f10d --- /dev/null +++ b/tests/ui/crashes/ice-6153.rs @@ -0,0 +1,9 @@ +pub struct S<'a, 'e>(&'a str, &'e str); + +pub type T<'a, 'e> = std::collections::HashMap, ()>; + +impl<'e, 'a: 'e> S<'a, 'e> { + pub fn foo(_a: &str, _b: &str, _map: &T) {} +} + +fn main() {} From 377d1fab1f1fe104c12cea17f7f24a8e23775942 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 11 Oct 2020 22:57:22 +0900 Subject: [PATCH 0743/1110] Remove the generated files by `update-references.sh` if they are empty --- doc/adding_lints.md | 3 ++- tests/ui-cargo/update-references.sh | 8 ++++++++ tests/ui-toml/update-references.sh | 8 ++++++++ tests/ui/update-references.sh | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 2869c3bf7d4..344bb455aa5 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -104,7 +104,8 @@ every time before running `tests/ui/update-all-references.sh`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit our lint, we need to commit the generated `.stderr` files, too. In general, you should only commit files changed by `tests/ui/update-all-references.sh` for the -specific lint you are creating/editing. +specific lint you are creating/editing. Note that if the generated files are +empty, they should be removed. ### Cargo lints diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh index 50d42678734..2ab51168bca 100755 --- a/tests/ui-cargo/update-references.sh +++ b/tests/ui-cargo/update-references.sh @@ -29,10 +29,18 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi done diff --git a/tests/ui-toml/update-references.sh b/tests/ui-toml/update-references.sh index 50d42678734..2ab51168bca 100755 --- a/tests/ui-toml/update-references.sh +++ b/tests/ui-toml/update-references.sh @@ -29,10 +29,18 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi done diff --git a/tests/ui/update-references.sh b/tests/ui/update-references.sh index 2c13c327d79..e16ed600ef8 100755 --- a/tests/ui/update-references.sh +++ b/tests/ui/update-references.sh @@ -30,15 +30,27 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then echo updating "$MYDIR"/"$FIXED_NAME" cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" + if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then + echo removing "$MYDIR"/"$FIXED_NAME" + rm "$MYDIR"/"$FIXED_NAME" + fi fi done From 6021c231599eabcb07b3a8207bddbb3796c93eee Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 11 Oct 2020 13:27:20 +0200 Subject: [PATCH 0744/1110] New lint: result-unit-err --- CHANGELOG.md | 1 + clippy_lints/src/functions.rs | 106 ++++++++++++++++++++++++++---- clippy_lints/src/lib.rs | 3 + src/lintlist/mod.rs | 7 ++ tests/ui/doc_errors.rs | 1 + tests/ui/doc_errors.stderr | 14 ++-- tests/ui/double_must_use.rs | 1 + tests/ui/double_must_use.stderr | 6 +- tests/ui/result_unit_error.rs | 38 +++++++++++ tests/ui/result_unit_error.stderr | 35 ++++++++++ 10 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 tests/ui/result_unit_error.rs create mode 100644 tests/ui/result_unit_error.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb6cf75d96..f21768c4498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1918,6 +1918,7 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 50b39cf4ea7..212a3100637 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,8 +1,9 @@ use crate::utils::{ - attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path, - must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, - trait_ref_of_method, type_is_unsafe_function, + attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, + span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, }; +use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -169,6 +171,52 @@ declare_clippy_lint! { "function or method that could take a `#[must_use]` attribute" } +declare_clippy_lint! { + /// **What it does:** Checks for public functions that return a `Result` + /// with an `Err` type of `()`. It suggests using a custom type that + /// implements [`std::error::Error`]. + /// + /// **Why is this bad?** Unit does not implement `Error` and carries no + /// further information about what went wrong. + /// + /// **Known problems:** Of course, this lint assumes that `Result` is used + /// for a fallible operation (which is after all the intended use). However + /// code may opt to (mis)use it as a basic two-variant-enum. In that case, + /// the suggestion is misguided, and the code should use a custom enum + /// instead. + /// + /// **Examples:** + /// ```rust + /// pub fn read_u8() -> Result { Err(()) } + /// ``` + /// should become + /// ```rust,should_panic + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct EndOfStream; + /// + /// impl fmt::Display for EndOfStream { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } + /// } + /// + /// impl std::error::Error for EndOfStream { } + /// + /// pub fn read_u8() -> Result { Err(EndOfStream) } + ///# fn main() { + ///# read_u8().unwrap(); + ///# } + /// ``` + /// + /// Note that there are crates that simplify creating the error type, e.g. + /// [`thiserror`](https://docs.rs/thiserror). + pub RESULT_UNIT_ERR, + style, + "public function returning `Result` with an `Err` type of `()`" +} + #[derive(Copy, Clone)] pub struct Functions { threshold: u64, @@ -188,6 +236,7 @@ impl_lint_pass!(Functions => [ MUST_USE_UNIT, DOUBLE_MUST_USE, MUST_USE_CANDIDATE, + RESULT_UNIT_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -233,15 +282,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attr = must_use_attr(&item.attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); return; } - if cx.access_levels.is_exported(item.hir_id) - && !is_proc_macro(cx.sess(), &item.attrs) - && attr_by_name(&item.attrs, "no_mangle").is_none() - { + if is_public && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() { check_must_use_candidate( cx, &sig.decl, @@ -257,11 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public && trait_ref_of_method(cx, item.hir_id).is_none() { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); - } else if cx.access_levels.is_exported(item.hir_id) + } else if is_public && !is_proc_macro(cx.sess(), &item.attrs) && trait_ref_of_method(cx, item.hir_id).is_none() { @@ -284,18 +338,21 @@ impl<'tcx> LateLintPass<'tcx> for Functions { if sig.header.abi == Abi::Rust { self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); } + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); } if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) - { + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), &item.attrs) { check_must_use_candidate( cx, &sig.decl, @@ -411,6 +468,29 @@ impl<'tcx> Functions { } } +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { + if_chain! { + if !in_external_macro(cx.sess(), item_span); + if let hir::FnRetTy::Return(ref ty) = decl.output; + if let hir::TyKind::Path(ref qpath) = ty.kind; + if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type)); + if let Some(ref args) = last_path_segment(qpath).args; + if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; + if let hir::TyKind::Tup(t) = err_ty.kind; + if t.is_empty(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "This returns a `Result<_, ()>", + None, + "Use a custom Error type instead", + ); + } + } +} + fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 097eca0af57..26a727687b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -582,6 +582,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, &functions::NOT_UNSAFE_PTR_ARG_DEREF, + &functions::RESULT_UNIT_ERR, &functions::TOO_MANY_ARGUMENTS, &functions::TOO_MANY_LINES, &future_not_send::FUTURE_NOT_SEND, @@ -1327,6 +1328,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), LintId::of(&identity_op::IDENTITY_OP), @@ -1558,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96c9d12d75f..d0fc8f0c8a9 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2005,6 +2005,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, + Lint { + name: "result_unit_err", + group: "style", + desc: "public function returning `Result` with an `Err` type of `()`", + deprecation: None, + module: "functions", + }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 445fc8d31d7..f47b81a450e 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,5 +1,6 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] +#![allow(clippy::result_unit_err)] use std::io; diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index f44d6693d30..c7b616e2897 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:6:1 + --> $DIR/doc_errors.rs:7:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:10:1 + --> $DIR/doc_errors.rs:11:1 | LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:15:1 + --> $DIR/doc_errors.rs:16:1 | LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:20:1 + --> $DIR/doc_errors.rs:21:1 | LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:50:5 + --> $DIR/doc_errors.rs:51:5 | LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:55:5 + --> $DIR/doc_errors.rs:56:5 | LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -49,7 +49,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:84:5 + --> $DIR/doc_errors.rs:85:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index a48e675e4ea..05e087b08bc 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -1,4 +1,5 @@ #![warn(clippy::double_must_use)] +#![allow(clippy::result_unit_err)] #[must_use] pub fn must_use_result() -> Result<(), ()> { diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr index bc37785294f..8290ece1cad 100644 --- a/tests/ui/double_must_use.stderr +++ b/tests/ui/double_must_use.stderr @@ -1,5 +1,5 @@ error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:4:1 + --> $DIR/double_must_use.rs:5:1 | LL | pub fn must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn must_use_result() -> Result<(), ()> { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:9:1 + --> $DIR/double_must_use.rs:10:1 | LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:14:1 + --> $DIR/double_must_use.rs:15:1 | LL | pub fn must_use_array() -> [Result<(), ()>; 1] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs new file mode 100644 index 00000000000..a66f581b215 --- /dev/null +++ b/tests/ui/result_unit_error.rs @@ -0,0 +1,38 @@ +#[warn(clippy::result_unit_err)] +#[allow(unused)] + +pub fn returns_unit_error() -> Result { + Err(()) +} + +fn private_unit_errors() -> Result { + Err(()) +} + +pub trait HasUnitError { + fn get_that_error(&self) -> Result; + + fn get_this_one_too(&self) -> Result { + Err(()) + } +} + +impl HasUnitError for () { + fn get_that_error(&self) -> Result { + Ok(true) + } +} + +trait PrivateUnitError { + fn no_problem(&self) -> Result; +} + +pub struct UnitErrorHolder; + +impl UnitErrorHolder { + pub fn unit_error(&self) -> Result { + Ok(0) + } +} + +fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr new file mode 100644 index 00000000000..986d9718acd --- /dev/null +++ b/tests/ui/result_unit_error.stderr @@ -0,0 +1,35 @@ +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:4:1 + | +LL | pub fn returns_unit_error() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:13:5 + | +LL | fn get_that_error(&self) -> Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:15:5 + | +LL | fn get_this_one_too(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:33:5 + | +LL | pub fn unit_error(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: aborting due to 4 previous errors + From 74ae116131696e4385d5b8e5da34deaad0d25ec9 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 11 Oct 2020 22:15:56 +0200 Subject: [PATCH 0745/1110] Use lowercase in error messages --- clippy_lints/src/functions.rs | 14 +++++++------- tests/ui/result_unit_error.stderr | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 212a3100637..fd45a6da61c 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -194,19 +194,19 @@ declare_clippy_lint! { /// use std::fmt; /// /// #[derive(Debug)] - /// struct EndOfStream; + /// pub struct EndOfStream; /// /// impl fmt::Display for EndOfStream { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "End of Stream") - /// } + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } /// } /// /// impl std::error::Error for EndOfStream { } /// /// pub fn read_u8() -> Result { Err(EndOfStream) } ///# fn main() { - ///# read_u8().unwrap(); + ///# read_u8().unwrap(); ///# } /// ``` /// @@ -483,9 +483,9 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span cx, RESULT_UNIT_ERR, fn_header_span, - "This returns a `Result<_, ()>", + "this returns a `Result<_, ()>", None, - "Use a custom Error type instead", + "use a custom Error type instead", ); } } diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 986d9718acd..b8230032491 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,35 +1,35 @@ -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:4:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:13:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:15:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:33:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead error: aborting due to 4 previous errors From c5774f94efd60b60fc7120ba3d6de7f79b05681b Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 12 Oct 2020 16:50:34 +0200 Subject: [PATCH 0746/1110] lintlist.rs: Replace lazy_static with once_cell Follow-up to https://github.com/rust-lang/rust-clippy/pull/6120 --- clippy_dev/src/update_lints.rs | 2 +- src/driver.rs | 1 + src/lintlist/mod.rs | 13 +++++++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index a9a70929942..556b67e0b37 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -29,7 +29,7 @@ pub fn run(update_mode: UpdateMode) { false, update_mode == UpdateMode::Change, || { - format!("pub static ref ALL_LINTS: Vec = vec!{:#?};", sorted_usable_lints) + format!("vec!{:#?}", sorted_usable_lints) .lines() .map(ToString::to_string) .collect::>() diff --git a/src/driver.rs b/src/driver.rs index 377f6d22446..c9b07855af1 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d0fc8f0c8a9..624223ff706 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1,15 +1,16 @@ -//! This file is managed by `cargo dev update_lints`. Do not edit. +//! This file is managed by `cargo dev update_lints`. Do not edit or format this file. -use lazy_static::lazy_static; +use std::lazy::SyncLazy; pub mod lint; pub use lint::Level; pub use lint::Lint; pub use lint::LINT_LEVELS; -lazy_static! { +#[rustfmt::skip] +pub static ALL_LINTS: SyncLazy> = SyncLazy::new(|| { // begin lint list, do not remove this comment, it’s used in `update_lints` -pub static ref ALL_LINTS: Vec = vec![ +vec![ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -2831,6 +2832,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, -]; +] // end lint list, do not remove this comment, it’s used in `update_lints` -} +}); From 098e4f119595cc199bf09ccf150aeefa6b2c49ac Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 12 Oct 2020 18:29:29 +0200 Subject: [PATCH 0747/1110] driver.rs: Replace lazy_static with once_cell --- Cargo.toml | 2 -- src/driver.rs | 16 +++++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7a3099b8ab..13db35f4b0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,13 +34,11 @@ clippy_lints = { version = "0.0.212", path = "clippy_lints" } semver = "0.10" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } -lazy_static = "1.0" [dev-dependencies] cargo_metadata = "0.11.1" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" -lazy_static = "1.0" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" diff --git a/src/driver.rs b/src/driver.rs index c9b07855af1..e32ba116939 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -18,13 +18,13 @@ use rustc_interface::interface; use rustc_middle::ty::TyCtxt; use rustc_tools_util::VersionInfo; -use lazy_static::lazy_static; use std::borrow::Cow; use std::env; use std::ops::Deref; use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; +use std::lazy::SyncLazy; mod lintlist; @@ -231,13 +231,11 @@ You can use tool lints to allow or deny lints from your code, eg.: const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new"; -lazy_static! { - static ref ICE_HOOK: Box) + Sync + Send + 'static> = { - let hook = panic::take_hook(); - panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); - hook - }; -} +static ICE_HOOK: SyncLazy) + Sync + Send + 'static>> = SyncLazy::new(|| { + let hook = panic::take_hook(); + panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); + hook +}); fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace @@ -296,7 +294,7 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option = env::args().collect(); From 7b3493c0e95e8cf9656d2cefffc621cb3e5eb726 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 12 Oct 2020 18:34:06 +0200 Subject: [PATCH 0748/1110] fmt --- src/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index e32ba116939..e5d740cecd3 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -20,11 +20,11 @@ use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; +use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; -use std::lazy::SyncLazy; mod lintlist; From 32fdb8fb0c15ddc202eed70b82babca8d529e39b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 8 Oct 2020 23:02:16 +0200 Subject: [PATCH 0749/1110] Lint on identical variable used as args in `assert_eq!` macro call --- clippy_lints/src/eq_op.rs | 37 ++++++++++++++++++++++++- clippy_lints/src/lib.rs | 2 ++ tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/double_parens.rs | 2 +- tests/ui/eq_op_early.rs | 15 ++++++++++ tests/ui/eq_op_early.stderr | 16 +++++++++++ tests/ui/used_underscore_binding.rs | 2 +- 7 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tests/ui/eq_op_early.rs create mode 100644 tests/ui/eq_op_early.stderr diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index e16ec783fab..7126c98a0b4 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,9 +1,13 @@ +use crate::utils::ast_utils::eq_expr; use crate::utils::{ eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; +use if_chain::if_chain; +use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -23,6 +27,12 @@ declare_clippy_lint! { /// # let x = 1; /// if x + 1 == x + 1 {} /// ``` + /// or + /// ```rust + /// # let a = 3; + /// # let b = 4; + /// assert_eq!(a, a); + /// ``` pub EQ_OP, correctness, "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)" @@ -52,6 +62,31 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); +impl EarlyLintPass for EqOp { + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + if_chain! { + if mac.path == sym!(assert_eq); + let tokens = mac.args.inner_tokens(); + let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + if let Ok(left) = parser.parse_expr(); + if parser.eat(&token::Comma); + if let Ok(right) = parser.parse_expr(); + let left_expr = left.into_inner(); + let right_expr = right.into_inner(); + if eq_expr(&left_expr, &right_expr); + + then { + span_lint( + cx, + EQ_OP, + left_expr.span.to(right_expr.span), + "identical args used in this `assert_eq!` macro call", + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fc4afde9d9e..dd99b6b9040 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,6 +348,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); + store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 3df8be6c232..e369f62f8bf 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] +#![allow(clippy::eq_op)] extern crate proc_macro; diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 9c7590c7dd6..ff1dc76ab63 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code)] +#![allow(dead_code, clippy::eq_op)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs new file mode 100644 index 00000000000..cf5660ea98d --- /dev/null +++ b/tests/ui/eq_op_early.rs @@ -0,0 +1,15 @@ +#![warn(clippy::eq_op)] + +fn main() { + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` (see #3574) + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr new file mode 100644 index 00000000000..9206e9026e9 --- /dev/null +++ b/tests/ui/eq_op_early.stderr @@ -0,0 +1,16 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:8:16 + | +LL | assert_eq!(a, a); + | ^^^^ + | + = note: `-D clippy::eq-op` implied by `-D warnings` + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:9:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 8e0243c49aa..d8bda7e8f48 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -3,7 +3,7 @@ #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::blacklisted_name, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] #[macro_use] From 8c28ba39b573c0d9be2ce7aa3cfc60757f3c81e6 Mon Sep 17 00:00:00 2001 From: Chris Ayoup Date: Mon, 12 Oct 2020 21:51:05 -0400 Subject: [PATCH 0750/1110] suggest a compatible shell for running setup-toolchain.sh setup-toolchain.sh uses "[[" which is a bash builtin, but the guide suggests running it with sh. On Ubuntu, /bin/sh points to dash and running the script as described fails. --- doc/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/basics.md b/doc/basics.md index 38959e2331b..f25edb793e2 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -46,7 +46,7 @@ this toolchain, you can just use the `setup-toolchain.sh` script or use `rustup-toolchain-install-master`: ```bash -sh setup-toolchain.sh +bash setup-toolchain.sh # OR cargo install rustup-toolchain-install-master # For better IDE integration also add `-c rustfmt -c rust-src` (optional) From a3e0446afe0ebd7a420f65cd6aec1c56687f0ef5 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 09:31:53 +0200 Subject: [PATCH 0751/1110] Extend to the `assert` macro family --- clippy_lints/src/eq_op.rs | 17 ++++++++++++++--- tests/ui/eq_op_early.rs | 25 +++++++++++++++++++++++- tests/ui/eq_op_early.stderr | 38 ++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 7126c98a0b4..a95d71042ee 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -7,6 +7,7 @@ use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,10 +65,20 @@ declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl EarlyLintPass for EqOp { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + let macro_list = [ + sym!(assert_eq), + sym!(assert_ne), + sym!(debug_assert_eq), + sym!(debug_assert_ne), + ]; if_chain! { - if mac.path == sym!(assert_eq); + if !in_external_macro(cx.sess, mac.span()); + if mac.path.segments.len() == 1; + let macro_name = mac.path.segments[0].ident.name; + if macro_list.contains(¯o_name); let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + let mut parser = parser::Parser::new( + &cx.sess.parse_sess, tokens, false, None); if let Ok(left) = parser.parse_expr(); if parser.eat(&token::Comma); if let Ok(right) = parser.parse_expr(); @@ -80,7 +91,7 @@ impl EarlyLintPass for EqOp { cx, EQ_OP, left_expr.span.to(right_expr.span), - "identical args used in this `assert_eq!` macro call", + &format!("identical args used in this `{}!` macro call", macro_name), ); } } diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs index cf5660ea98d..25e1c6ac6b7 100644 --- a/tests/ui/eq_op_early.rs +++ b/tests/ui/eq_op_early.rs @@ -7,9 +7,32 @@ fn main() { // lint identical args in `assert_eq!` (see #3574) assert_eq!(a, a); assert_eq!(a + 1, a + 1); - // ok assert_eq!(a, b); assert_eq!(a, a + 1); assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); } diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr index 9206e9026e9..1df094fae18 100644 --- a/tests/ui/eq_op_early.stderr +++ b/tests/ui/eq_op_early.stderr @@ -12,5 +12,41 @@ error: identical args used in this `assert_eq!` macro call LL | assert_eq!(a + 1, a + 1); | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:16:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:17:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:24:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:25:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:32:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:33:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 8 previous errors From e2124086b8107a59129e163aa120dec50add0f77 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 11:17:51 +0200 Subject: [PATCH 0752/1110] Fix FP in `same_functions_in_if_condition` lint about condition as macro --- clippy_lints/src/copies.rs | 6 +++++- tests/ui/same_functions_in_if_condition.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 10a64769585..6c969c3ead0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,4 +1,4 @@ -use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; +use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; @@ -220,6 +220,10 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { }; let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if in_macro(lhs.span) || in_macro(rhs.span) { + return false; + } // Do not spawn warning if `IFS_SAME_COND` already produced it. if eq_expr_value(cx, lhs, rhs) { return false; diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 686867cf5c6..7f28f025790 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -77,4 +77,14 @@ fn ifs_same_cond_fn() { } } -fn main() {} +fn main() { + // macro as condition (see #6168) + let os = if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "windows") { + "windows" + } else { + "linux" + }; + println!("{}", os); +} From 121a047645270d5e9ac965d57c324301ea1f21c0 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 23:46:23 +0200 Subject: [PATCH 0753/1110] Move linting of `assert` macros from early to late pass --- clippy_lints/src/eq_op.rs | 73 +++++++++++++--------------- clippy_lints/src/lib.rs | 2 - tests/ui/eq_op.rs | 53 +++++++++++++++++++++ tests/ui/eq_op.stderr | 95 ++++++++++++++++++++++++++++++++++++- tests/ui/eq_op_early.rs | 38 --------------- tests/ui/eq_op_early.stderr | 52 -------------------- 6 files changed, 179 insertions(+), 134 deletions(-) delete mode 100644 tests/ui/eq_op_early.rs delete mode 100644 tests/ui/eq_op_early.stderr diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index a95d71042ee..9653e62cad0 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,14 +1,11 @@ -use crate::utils::ast_utils::eq_expr; use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, + eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::{ast, token}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_parse::parser; +use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -63,44 +60,38 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -impl EarlyLintPass for EqOp { - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - let macro_list = [ - sym!(assert_eq), - sym!(assert_ne), - sym!(debug_assert_eq), - sym!(debug_assert_ne), - ]; - if_chain! { - if !in_external_macro(cx.sess, mac.span()); - if mac.path.segments.len() == 1; - let macro_name = mac.path.segments[0].ident.name; - if macro_list.contains(¯o_name); - let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new( - &cx.sess.parse_sess, tokens, false, None); - if let Ok(left) = parser.parse_expr(); - if parser.eat(&token::Comma); - if let Ok(right) = parser.parse_expr(); - let left_expr = left.into_inner(); - let right_expr = right.into_inner(); - if eq_expr(&left_expr, &right_expr); - - then { - span_lint( - cx, - EQ_OP, - left_expr.span.to(right_expr.span), - &format!("identical args used in this `{}!` macro call", macro_name), - ); - } - } - } -} +const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Block(ref block, _) = e.kind { + for stmt in block.stmts { + for amn in &ASSERT_MACRO_NAMES { + if_chain! { + if is_expn_of(stmt.span, amn).is_some(); + if let StmtKind::Semi(ref matchexpr) = stmt.kind; + if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; + if let Some(ref matchheader) = matchblock.expr; + if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; + if let ExprKind::Tup(ref conditions) = headerexpr.kind; + if conditions.len() == 2; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if eq_expr_value(cx, lhs, rhs); + + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", amn), + ); + } + } + } + } + } if let ExprKind::Binary(op, ref left, ref right) = e.kind { if e.span.from_expansion() { return; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dd99b6b9040..fc4afde9d9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,7 +348,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); - store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -911,7 +910,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); - store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31..3ab4dfc439b 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,6 +60,8 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; + + check_assert_identical_args(); } #[rustfmt::skip] @@ -85,3 +87,54 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn check_assert_identical_args() { + // lint also in macro definition + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078ee..21a63aec7a1 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,98 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:94:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `#[deny(clippy::eq_op)]` on by default + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:95:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:110:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:111:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:118:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:119:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:96:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:97:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:126:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:127:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:134:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:135:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 39 previous errors diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs deleted file mode 100644 index 25e1c6ac6b7..00000000000 --- a/tests/ui/eq_op_early.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![warn(clippy::eq_op)] - -fn main() { - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` (see #3574) - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); -} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr deleted file mode 100644 index 1df094fae18..00000000000 --- a/tests/ui/eq_op_early.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:8:16 - | -LL | assert_eq!(a, a); - | ^^^^ - | - = note: `-D clippy::eq-op` implied by `-D warnings` - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:9:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:16:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:17:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:24:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:25:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:32:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:33:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 8 previous errors - From e82264860dd275fe95c335929ee9a231c3a61236 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 14 Oct 2020 23:15:01 +1100 Subject: [PATCH 0754/1110] Add a known problem for transmute_ptr_to_ref lint --- clippy_lints/src/transmute.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index c75adb62f25..47c650ac27d 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -98,7 +98,11 @@ declare_clippy_lint! { /// /// **Why is this bad?** This can always be rewritten with `&` and `*`. /// - /// **Known problems:** None. + /// **Known problems:** + /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0, + /// while dereferencing raw pointer is not stable yet. + /// If you need to do this in those places, + /// you would have to use `transmute` instead. /// /// **Example:** /// ```rust,ignore From 8ba18aeb6963d70767a0880ecb7929864fe14ef9 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Wed, 14 Oct 2020 11:58:22 +0200 Subject: [PATCH 0755/1110] README: sort en/disabling section, fix typos, add note --- README.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 62a8be0abf2..e1b3c84d691 100644 --- a/README.md +++ b/README.md @@ -169,12 +169,33 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: Note: `deny` produces errors instead of warnings. -If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra -flags to Clippy during the run: `cargo clippy -- -A clippy::lint_name` will run Clippy with `lint_name` disabled and -`cargo clippy -- -W clippy::lint_name` will run it with that enabled. This also works with lint groups. For example you -can run Clippy with warnings for all lints enabled: `cargo clippy -- -W clippy::pedantic` +If you do not want to include your lint levels in your code, you can globally enable/disable lints +by passing extra flags to Clippy during the run: + +To disable `lint_name`, run + +```terminal +cargo clippy -- -A clippy::lint_name +``` + +And to enable `lint_name`, run + +```terminal +cargo clippy -- -W clippy::lint_name +``` + +This also works with lint groups. For example you +can run Clippy with warnings for all lints enabled: +```terminal +cargo clippy -- -W clippy::pedantic +``` + If you care only about a single lint, you can allow all others and then explicitly reenable -the lint(s) you are interested in: `cargo clippy -- -Aclippy::all -Wclippy::useless_format -Wclippy::...` +the lint(s) you are interested in: +```terminal +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... +``` +Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ## Contributing From 71c29b5be8526562c3de8d3b7dc94611647ee120 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 14 Oct 2020 21:29:53 +0200 Subject: [PATCH 0756/1110] Add iterator test case for `eq_op` lint --- tests/ui/eq_op.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 3ab4dfc439b..20613ac6afe 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -137,4 +137,8 @@ fn check_assert_identical_args() { debug_assert_ne!(a, b); debug_assert_ne!(a, a + 1); debug_assert_ne!(a + 1, b + 1); + + let my_vec = vec![1; 5]; + let mut my_iter = my_vec.iter(); + assert_ne!(my_iter.next(), my_iter.next()); } From 07b2da884cda8103af50beb327723dec8204fc61 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 6 Oct 2020 11:49:08 +0200 Subject: [PATCH 0757/1110] add lint less_concise_than_option_unwrap_or --- CHANGELOG.md | 1 + clippy_lints/src/less_concise_than.rs | 107 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + clippy_lints/src/option_if_let_else.rs | 53 +----------- clippy_lints/src/utils/eager_or_lazy.rs | 2 +- clippy_lints/src/utils/usage.rs | 50 ++++++++++- src/lintlist/mod.rs | 7 ++ tests/ui/less_concise_than.fixed | 43 ++++++++++ tests/ui/less_concise_than.rs | 55 ++++++++++++ tests/ui/less_concise_than.stderr | 52 ++++++++++++ tests/ui/shadow.rs | 1 + tests/ui/shadow.stderr | 94 ++++++++++----------- 12 files changed, 369 insertions(+), 100 deletions(-) create mode 100644 clippy_lints/src/less_concise_than.rs create mode 100644 tests/ui/less_concise_than.fixed create mode 100644 tests/ui/less_concise_than.rs create mode 100644 tests/ui/less_concise_than.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f21768c4498..93ce6bb85d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,6 +1781,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/less_concise_than.rs new file mode 100644 index 00000000000..097aff4b178 --- /dev/null +++ b/clippy_lints/src/less_concise_than.rs @@ -0,0 +1,107 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// match int_optional { + /// Some(v) => v, + /// None => 1, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// int_optional.unwrap_or(1) + /// ``` + pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" +} + +declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); + +impl LateLintPass<'_> for LessConciseThan { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if utils::in_macro(expr.span) { + return; + } + if lint_option_unwrap_or_case(cx, expr) { + return; + } + } +} + +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + #[allow(clippy::needless_bool)] + fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + if_chain! { + if arms.len() == 2; + if arms.iter().all(|arm| arm.guard.is_none()); + if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| + if_chain! { + if let PatKind::Path(ref qpath) = arm.pat.kind; + if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); + then { true } + else { false } + } + ); + let some_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; + if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); + if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + if let def::Res::Local(body_path_hir_id) = body_path.res; + if body_path_hir_id == binding_hir_id; + then { Some(none_arm) } + else { None } + } + } + if_chain! { + if !utils::usage::contains_return_break_continue_macro(expr); + if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(match_expr); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, + "this pattern can be more concisely encoded with `Option::unwrap_or`", + "replace with", + format!( + "{}.{}({}{})", + match_expr_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { false} + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26a727687b1..2e9900815d9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,6 +224,7 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; +mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -609,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, + &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -1126,6 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box less_concise_than::LessConciseThan); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1210,6 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 383a62da821..eb7624b25a3 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,10 +5,8 @@ use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,53 +82,6 @@ struct OptionIfLetElseOccurence { wrap_braces: bool, } -struct ReturnBreakContinueMacroVisitor { - seen_return_break_continue: bool, -} - -impl ReturnBreakContinueMacroVisitor { - fn new() -> ReturnBreakContinueMacroVisitor { - ReturnBreakContinueMacroVisitor { - seen_return_break_continue: false, - } - } -} - -impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self.seen_return_break_continue { - // No need to look farther if we've already seen one of them - return; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - self.seen_return_break_continue = true; - }, - // Something special could be done here to handle while or for loop - // desugaring, as this will detect a break if there's a while loop - // or a for loop inside the expression. - _ => { - if utils::in_macro(ex.span) { - self.seen_return_break_continue = true; - } else { - rustc_hir::intravisit::walk_expr(self, ex); - } - }, - } - } -} - -fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); - recursive_visitor.visit_expr(expression); - recursive_visitor.seen_return_break_continue -} - /// Extracts the body of a given arm. If the arm contains only an expression, /// then it returns the expression. Otherwise, it returns the entire block fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { @@ -208,8 +159,8 @@ fn detect_option_if_let_else<'tcx>( if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(arms[0].body); - if !contains_return_break_continue_macro(arms[1].body); + if !utils::usage::contains_return_break_continue_macro(arms[0].body); + if !utils::usage::contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 6938d9971d9..30e812c284b 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -82,7 +82,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { /// Identify some potentially computationally expensive patterns. /// This function is named so to stress that its implementation is non-exhaustive. /// It returns FNs and FPs. -fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { // Searches an expression for method calls or function calls that aren't ctors struct FunCallFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index ea1dc3be29b..2fd6046ebcf 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,10 +1,11 @@ +use crate::utils; use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, HirId, Path}; +use rustc_hir::{Expr, ExprKind, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -174,3 +175,50 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } + +struct ReturnBreakContinueMacroVisitor { + seen_return_break_continue: bool, +} + +impl ReturnBreakContinueMacroVisitor { + fn new() -> ReturnBreakContinueMacroVisitor { + ReturnBreakContinueMacroVisitor { + seen_return_break_continue: false, + } + } +} + +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + if utils::in_macro(ex.span) { + self.seen_return_break_continue = true; + } else { + rustc_hir::intravisit::walk_expr(self, ex); + } + }, + } + } +} + +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { + let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 624223ff706..6dc95fcfdb2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,6 +1075,13 @@ vec![ deprecation: None, module: "len_zero", }, + Lint { + name: "less_concise_than_option_unwrap_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + deprecation: None, + module: "less_concise_than", + }, Lint { name: "let_and_return", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/less_concise_than.fixed new file mode 100644 index 00000000000..52b69ebba3e --- /dev/null +++ b/tests/ui/less_concise_than.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + Some(1).unwrap_or(42); + + // richer none expr + Some(1).unwrap_or_else(|| 1 + 42); + + // multiline case + Some(1).unwrap_or_else(|| { + let a = 1 + 42; + let b = a + 42; + b + 42 + }); + + // string case + Some("Bob").unwrap_or("Alice"); + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.rs b/tests/ui/less_concise_than.rs new file mode 100644 index 00000000000..bb2a8f2050a --- /dev/null +++ b/tests/ui/less_concise_than.rs @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + match Some(1) { + Some(i) => i, + None => 42, + }; + + // richer none expr + match Some(1) { + Some(i) => i, + None => 1 + 42, + }; + + // multiline case + match Some(1) { + Some(i) => i, + None => { + let a = 1 + 42; + let b = a + 42; + b + 42 + }, + }; + + // string case + match Some("Bob") { + Some(i) => i, + None => "Alice", + }; + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/less_concise_than.stderr new file mode 100644 index 00000000000..e3e8a406db1 --- /dev/null +++ b/tests/ui/less_concise_than.stderr @@ -0,0 +1,52 @@ +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:7:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + | + = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:13:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:19:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => { +LL | | let a = 1 + 42; +... | +LL | | }, +LL | | }; + | |_____^ + | +help: replace with + | +LL | Some(1).unwrap_or_else(|| { +LL | let a = 1 + 42; +LL | let b = a + 42; +LL | b + 42 +LL | }); + | + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:29:5 + | +LL | / match Some("Bob") { +LL | | Some(i) => i, +LL | | None => "Alice", +LL | | }; + | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index bd91ae4e934..e7441293d45 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,6 +8,7 @@ #![allow( unused_parens, unused_variables, + clippy::less_concise_than_option_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 8a831375b41..7c1ad2949e9 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,135 +1,135 @@ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:26:5 + --> $DIR/shadow.rs:27:5 | LL | let x = &mut x; | ^^^^^^^^^^^^^^^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:25:13 + --> $DIR/shadow.rs:26:13 | LL | let mut x = 1; | ^ error: `x` is shadowed by itself in `{ x }` - --> $DIR/shadow.rs:27:5 - | -LL | let x = { x }; - | ^^^^^^^^^^^^^^ - | -note: previous binding is here - --> $DIR/shadow.rs:26:9 - | -LL | let x = &mut x; - | ^ - -error: `x` is shadowed by itself in `(&*x)` --> $DIR/shadow.rs:28:5 | -LL | let x = (&*x); +LL | let x = { x }; | ^^^^^^^^^^^^^^ | note: previous binding is here --> $DIR/shadow.rs:27:9 | +LL | let x = &mut x; + | ^ + +error: `x` is shadowed by itself in `(&*x)` + --> $DIR/shadow.rs:29:5 + | +LL | let x = (&*x); + | ^^^^^^^^^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:28:9 + | LL | let x = { x }; | ^ error: `x` is shadowed by `{ *x + 1 }` which reuses the original value - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = { *x + 1 }; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: initialization happens here - --> $DIR/shadow.rs:29:13 + --> $DIR/shadow.rs:30:13 | LL | let x = { *x + 1 }; | ^^^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:28:9 + --> $DIR/shadow.rs:29:9 | LL | let x = (&*x); | ^ error: `x` is shadowed by `id(x)` which reuses the original value - --> $DIR/shadow.rs:30:9 - | -LL | let x = id(x); - | ^ - | -note: initialization happens here - --> $DIR/shadow.rs:30:13 - | -LL | let x = id(x); - | ^^^^^ -note: previous binding is here - --> $DIR/shadow.rs:29:9 - | -LL | let x = { *x + 1 }; - | ^ - -error: `x` is shadowed by `(1, x)` which reuses the original value --> $DIR/shadow.rs:31:9 | -LL | let x = (1, x); +LL | let x = id(x); | ^ | note: initialization happens here --> $DIR/shadow.rs:31:13 | -LL | let x = (1, x); - | ^^^^^^ +LL | let x = id(x); + | ^^^^^ note: previous binding is here --> $DIR/shadow.rs:30:9 | -LL | let x = id(x); +LL | let x = { *x + 1 }; | ^ -error: `x` is shadowed by `first(x)` which reuses the original value +error: `x` is shadowed by `(1, x)` which reuses the original value --> $DIR/shadow.rs:32:9 | -LL | let x = first(x); +LL | let x = (1, x); | ^ | note: initialization happens here --> $DIR/shadow.rs:32:13 | +LL | let x = (1, x); + | ^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:31:9 + | +LL | let x = id(x); + | ^ + +error: `x` is shadowed by `first(x)` which reuses the original value + --> $DIR/shadow.rs:33:9 + | +LL | let x = first(x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:33:13 + | LL | let x = first(x); | ^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:32:9 | LL | let x = (1, x); | ^ error: `x` is being shadowed - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:35:9 | LL | let x = y; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: initialization happens here - --> $DIR/shadow.rs:34:13 + --> $DIR/shadow.rs:35:13 | LL | let x = y; | ^ note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:33:9 | LL | let x = first(x); | ^ error: `x` shadows a previous declaration - --> $DIR/shadow.rs:36:5 + --> $DIR/shadow.rs:37:5 | LL | let x; | ^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:35:9 | LL | let x = y; | ^ From 9c9327980becadc15a68307705b3a06c28116ae1 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:42:45 +0200 Subject: [PATCH 0758/1110] manual-unwrap-or / rename files --- clippy_lints/src/{less_concise_than.rs => manual_unwrap_or.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/{less_concise_than.rs => manual_unwrap_or.rs} (100%) diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/manual_unwrap_or.rs similarity index 100% rename from clippy_lints/src/less_concise_than.rs rename to clippy_lints/src/manual_unwrap_or.rs From 6d4eeeabcda6d6d25738e1e8e2b64580daefc4b9 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:55:05 +0200 Subject: [PATCH 0759/1110] manual-unwrap-or / pr remarks --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 9 +- clippy_lints/src/manual_unwrap_or.rs | 113 +++++++++--------- src/lintlist/mod.rs | 14 +-- ...cise_than.fixed => manual_unwrap_or.fixed} | 4 +- ...ss_concise_than.rs => manual_unwrap_or.rs} | 7 +- ...se_than.stderr => manual_unwrap_or.stderr} | 29 +++-- tests/ui/shadow.rs | 2 +- 8 files changed, 101 insertions(+), 79 deletions(-) rename tests/ui/{less_concise_than.fixed => manual_unwrap_or.fixed} (93%) rename tests/ui/{less_concise_than.rs => manual_unwrap_or.rs} (90%) rename tests/ui/{less_concise_than.stderr => manual_unwrap_or.stderr} (54%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ce6bb85d8..d82f970b8bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,7 +1781,6 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero -[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use @@ -1797,6 +1796,7 @@ Released 2018-09-13 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2e9900815d9..d4d2f92a6a6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,7 +224,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -235,6 +234,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod manual_strip; +mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_identity; @@ -610,7 +610,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -642,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_strip::MANUAL_STRIP, + &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, &map_err_ignore::MAP_ERR_IGNORE, &map_identity::MAP_IDENTITY, @@ -1128,7 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); - store.register_late_pass(|| box less_concise_than::LessConciseThan); + store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1213,7 +1213,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), @@ -1371,6 +1370,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1666,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 097aff4b178..9d8fc863424 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,12 +2,14 @@ use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -16,7 +18,7 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_optional { + /// match int_option { /// Some(v) => v, /// None => 1, /// } @@ -24,39 +26,35 @@ declare_clippy_lint! { /// /// Use instead: /// ```rust - /// int_optional.unwrap_or(1) + /// int_option.unwrap_or(1) /// ``` - pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, - pedantic, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" } -declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); +declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for LessConciseThan { +impl LateLintPass<'_> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if utils::in_macro(expr.span) { - return; - } - if lint_option_unwrap_or_case(cx, expr) { + if in_external_macro(cx.sess(), expr.span) { return; } + lint_option_unwrap_or_case(cx, expr); } } fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - #[allow(clippy::needless_bool)] fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if_chain! { - if let PatKind::Path(ref qpath) = arm.pat.kind; - if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); - then { true } - else { false } - } + if let PatKind::Path(ref qpath) = arm.pat.kind { + utils::match_qpath(qpath, &utils::paths::OPTION_NONE) + } else { + false + } ); let some_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; @@ -65,43 +63,50 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - then { Some(none_arm) } - else { None } + if !utils::usage::contains_return_break_continue_macro(none_arm.body); + then { + Some(none_arm) + } + else { + None + } } } + if_chain! { - if !utils::usage::contains_return_break_continue_macro(expr); - if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(match_expr); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); - if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); - if let Some(indent) = utils::indent_of(cx, expr.span); - then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; - utils::span_lint_and_sugg( - cx, - LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, - "this pattern can be more concisely encoded with `Option::unwrap_or`", - "replace with", - format!( - "{}.{}({}{})", - match_expr_snippet, - method, - if eager_eval { ""} else { "|| " }, - reindented_none_body - ), - Applicability::MachineApplicable, - ); - true - } else { false} + if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(scrutinee); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `Option::{}`", &method), + "replace with", + format!( + "{}.{}({}{})", + scrutinee_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { + false + } } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6dc95fcfdb2..debd3c31d8b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,13 +1075,6 @@ vec![ deprecation: None, module: "len_zero", }, - Lint { - name: "less_concise_than_option_unwrap_or", - group: "pedantic", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", - deprecation: None, - module: "less_concise_than", - }, Lint { name: "let_and_return", group: "style", @@ -1187,6 +1180,13 @@ vec![ deprecation: None, module: "swap", }, + Lint { + name: "manual_unwrap_or", + group: "complexity", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + deprecation: None, + module: "manual_unwrap_or", + }, Lint { name: "many_single_char_names", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/manual_unwrap_or.fixed similarity index 93% rename from tests/ui/less_concise_than.fixed rename to tests/ui/manual_unwrap_or.fixed index 52b69ebba3e..99d30360db1 100644 --- a/tests/ui/less_concise_than.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,11 +1,13 @@ // run-rustfix -#![warn(clippy::less_concise_than_option_unwrap_or)] #![allow(dead_code)] fn unwrap_or() { // int case Some(1).unwrap_or(42); + // int case reversed + Some(1).unwrap_or(42); + // richer none expr Some(1).unwrap_or_else(|| 1 + 42); diff --git a/tests/ui/less_concise_than.rs b/tests/ui/manual_unwrap_or.rs similarity index 90% rename from tests/ui/less_concise_than.rs rename to tests/ui/manual_unwrap_or.rs index bb2a8f2050a..5d03d9db163 100644 --- a/tests/ui/less_concise_than.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::less_concise_than_option_unwrap_or)] #![allow(dead_code)] fn unwrap_or() { @@ -9,6 +8,12 @@ fn unwrap_or() { None => 42, }; + // int case reversed + match Some(1) { + None => 42, + Some(i) => i, + }; + // richer none expr match Some(1) { Some(i) => i, diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/manual_unwrap_or.stderr similarity index 54% rename from tests/ui/less_concise_than.stderr rename to tests/ui/manual_unwrap_or.stderr index e3e8a406db1..03da118a0c4 100644 --- a/tests/ui/less_concise_than.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -1,5 +1,5 @@ -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:7:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:6:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -7,10 +7,19 @@ LL | | None => 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` | - = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` + = note: `-D clippy::manual-unwrap-or` implied by `-D warnings` -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:13:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:12:5 + | +LL | / match Some(1) { +LL | | None => 42, +LL | | Some(i) => i, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:18:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -18,8 +27,8 @@ LL | | None => 1 + 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:19:5 +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:24:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -39,8 +48,8 @@ LL | b + 42 LL | }); | -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:29:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:34:5 | LL | / match Some("Bob") { LL | | Some(i) => i, @@ -48,5 +57,5 @@ LL | | None => "Alice", LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index e7441293d45..e366c75335c 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,7 +8,7 @@ #![allow( unused_parens, unused_variables, - clippy::less_concise_than_option_unwrap_or, + clippy::manual_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] From fc846c37fcc720c4a5c2e2075102c1957433e703 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 12 Oct 2020 00:06:21 +0200 Subject: [PATCH 0760/1110] manual_unwrap_or / use consts::constant_simple helper --- clippy_lints/src/manual_unwrap_or.rs | 11 +++++++---- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.stderr | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 9d8fc863424..ced941fac1a 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,3 +1,4 @@ +use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; @@ -18,15 +19,17 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_option { + /// let foo: Option = None; + /// match foo { /// Some(v) => v, /// None => 1, - /// } + /// }; /// ``` /// /// Use instead: /// ```rust - /// int_option.unwrap_or(1) + /// let foo: Option = None; + /// foo.unwrap_or(1); /// ``` pub MANUAL_UNWRAP_OR, complexity, @@ -84,7 +87,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); let method = if eager_eval { "unwrap_or" } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 99d30360db1..a9cc8678c9d 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -9,7 +9,7 @@ fn unwrap_or() { Some(1).unwrap_or(42); // richer none expr - Some(1).unwrap_or_else(|| 1 + 42); + Some(1).unwrap_or(1 + 42); // multiline case Some(1).unwrap_or_else(|| { diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 03da118a0c4..8f6835ed78d 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -18,14 +18,14 @@ LL | | Some(i) => i, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` -error: this pattern reimplements `Option::unwrap_or_else` +error: this pattern reimplements `Option::unwrap_or` --> $DIR/manual_unwrap_or.rs:18:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => 1 + 42, LL | | }; - | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or_else` --> $DIR/manual_unwrap_or.rs:24:5 From a8fb69f065a427f5d3fc7222b834cad9a2a7a712 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 13 Oct 2020 10:24:00 +0200 Subject: [PATCH 0761/1110] manual-unwrap-or / more pr remarks --- clippy_lints/src/manual_unwrap_or.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index ced941fac1a..719a8b91f66 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -47,7 +47,7 @@ impl LateLintPass<'_> for ManualUnwrapOr { } } -fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; @@ -69,8 +69,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if !utils::usage::contains_return_break_continue_macro(none_arm.body); then { Some(none_arm) - } - else { + } else { None } } @@ -102,14 +101,11 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc "{}.{}({}{})", scrutinee_snippet, method, - if eager_eval { ""} else { "|| " }, + if eager_eval { "" } else { "|| " }, reindented_none_body ), Applicability::MachineApplicable, ); - true - } else { - false } } } From 690a6a6c0eff1a3edeb5f4c2dcbf9994760c3184 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 14 Oct 2020 22:09:28 +0200 Subject: [PATCH 0762/1110] manual-unwrap-or / remove unwrap_or_else suggestion due to ownership issues --- clippy_lints/src/manual_unwrap_or.rs | 17 +++++---------- src/lintlist/mod.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 31 ++++++++++++++++++++++++---- tests/ui/manual_unwrap_or.rs | 31 ++++++++++++++++++++++++---- tests/ui/manual_unwrap_or.stderr | 18 ++++++++-------- 5 files changed, 69 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 719a8b91f66..ddb8cc25077 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -83,26 +83,19 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `Option::{}`", &method), + "this pattern reimplements `Option::unwrap_or`", "replace with", format!( - "{}.{}({}{})", + "{}.unwrap_or({})", scrutinee_snippet, - method, - if eager_eval { "" } else { "|| " }, - reindented_none_body + reindented_none_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index debd3c31d8b..6301d623a2b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a9cc8678c9d..a8736f1e6ef 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -12,10 +12,11 @@ fn unwrap_or() { Some(1).unwrap_or(1 + 42); // multiline case - Some(1).unwrap_or_else(|| { - let a = 1 + 42; - let b = a + 42; - b + 42 + #[rustfmt::skip] + Some(1).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 }); // string case @@ -40,6 +41,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 5d03d9db163..bede8cffc32 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -21,13 +21,14 @@ fn unwrap_or() { }; // multiline case + #[rustfmt::skip] match Some(1) { Some(i) => i, None => { - let a = 1 + 42; - let b = a + 42; - b + 42 - }, + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } }; // string case @@ -55,6 +56,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 8f6835ed78d..674f2952635 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -27,29 +27,29 @@ LL | | None => 1 + 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` -error: this pattern reimplements `Option::unwrap_or_else` - --> $DIR/manual_unwrap_or.rs:24:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:25:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => { -LL | | let a = 1 + 42; +LL | | 42 + 42 ... | -LL | | }, +LL | | } LL | | }; | |_____^ | help: replace with | -LL | Some(1).unwrap_or_else(|| { -LL | let a = 1 + 42; -LL | let b = a + 42; -LL | b + 42 +LL | Some(1).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 LL | }); | error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:34:5 + --> $DIR/manual_unwrap_or.rs:35:5 | LL | / match Some("Bob") { LL | | Some(i) => i, From 2da121d97fa2a1839d703e8c584d5bdf989b8117 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 14 Oct 2020 23:26:48 +0200 Subject: [PATCH 0763/1110] Fix remark linting on checkboxes --- .github/PULL_REQUEST_TEMPLATE.md | 12 ++++++------ doc/adding_lints.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 137a7363094..6c92e10522c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,12 +12,12 @@ your PR is merged. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. -- [ ] Followed [lint naming conventions][lint_naming] -- [ ] Added passing UI tests (including committed `.stderr` file) -- [ ] `cargo test` passes locally -- [ ] Executed `cargo dev update_lints` -- [ ] Added lint documentation -- [ ] Run `cargo dev fmt` +- \[ ] Followed [lint naming conventions][lint_naming] +- \[ ] Added passing UI tests (including committed `.stderr` file) +- \[ ] `cargo test` passes locally +- \[ ] Executed `cargo dev update_lints` +- \[ ] Added lint documentation +- \[ ] Run `cargo dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 344bb455aa5..ab8ff711796 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -454,12 +454,12 @@ Before submitting your PR make sure you followed all of the basic requirements: -- [ ] Followed [lint naming conventions][lint_naming] -- [ ] Added passing UI tests (including committed `.stderr` file) -- [ ] `cargo test` passes locally -- [ ] Executed `cargo dev update_lints` -- [ ] Added lint documentation -- [ ] Run `cargo dev fmt` +- \[ ] Followed [lint naming conventions][lint_naming] +- \[ ] Added passing UI tests (including committed `.stderr` file) +- \[ ] `cargo test` passes locally +- \[ ] Executed `cargo dev update_lints` +- \[ ] Added lint documentation +- \[ ] Run `cargo dev fmt` ## Cheatsheet From 32e2021b75f5bb5c83bf753de76ec9ed499d12cc Mon Sep 17 00:00:00 2001 From: Chris Ayoup Date: Wed, 14 Oct 2020 23:49:48 -0400 Subject: [PATCH 0764/1110] Lint items after statements in macro expansions The items_after_statements lint was skipping all expansions. Instead we should still lint local macros. Fixes #578 --- clippy_lints/src/items_after_statements.rs | 7 ++++--- tests/ui/item_after_statement.rs | 5 ++++- tests/ui/item_after_statement.stderr | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index c8576bcfcb4..8998fae09de 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -2,7 +2,8 @@ use crate::utils::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -53,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); impl EarlyLintPass for ItemsAfterStatements { fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { - if item.span.from_expansion() { + if in_external_macro(cx.sess(), item.span) { return; } @@ -67,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements { // lint on all further items for stmt in stmts { if let StmtKind::Item(ref it) = *stmt { - if it.span.from_expansion() { + if in_external_macro(cx.sess(), it.span) { return; } if let ItemKind::MacroDef(..) = it.kind { diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index c17a7cbc8d9..377e58e4417 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -28,7 +28,10 @@ fn mac() { // do not lint this, because it needs to be after `a` macro_rules! b { () => {{ - a = 6 + a = 6; + fn say_something() { + println!("something"); + } }}; } b!(); diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index f8f010b5e5c..68a3c81b6a8 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -16,5 +16,18 @@ LL | | println!("foo"); LL | | } | |_____^ -error: aborting due to 2 previous errors +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:32:13 + | +LL | / fn say_something() { +LL | | println!("something"); +LL | | } + | |_____________^ +... +LL | b!(); + | ----- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors From ef91de640294e6d0fbf881082196ba83379ea447 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 15 Oct 2020 22:37:53 -0700 Subject: [PATCH 0765/1110] Run cargo dev fmt Signed-off-by: Joe Richey --- clippy_lints/src/future_not_send.rs | 9 ++------- clippy_lints/src/trivially_copy_pass_by_ref.rs | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index d2a322e1223..71a30d1c33d 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await( - db, - &obligation, - ); - if let Trait(trait_pred, _) = - obligation.predicate.skip_binders() - { + infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred, _) = obligation.predicate.skip_binders() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index d92eb86fb2e..e90ea0fc200 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -12,8 +12,8 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_target::abi::LayoutOf; -use rustc_target::spec::Target; use rustc_target::spec::abi::Abi; +use rustc_target::spec::Target; declare_clippy_lint! { /// **What it does:** Checks for functions taking arguments by reference, where From 6d358d29b0eb4e6f21526ccfb29636dea20d8993 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 16 Oct 2020 14:23:17 +0200 Subject: [PATCH 0766/1110] Update semver 0.10 -> 0.11 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7a3099b8ab..e67aba19b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ path = "src/driver.rs" # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update -semver = "0.10" +semver = "0.11" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index fcf817b82c8..cd9363a8572 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -27,7 +27,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" -semver = "0.10.0" +semver = "0.11" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } From 701c7e2fbac1f05064519f0800128ea92491689a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 16 Oct 2020 22:11:37 +0200 Subject: [PATCH 0767/1110] bump cargo_metadata version --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 96655e7f5b9..1ddcd18598d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } [dev-dependencies] -cargo_metadata = "0.11.1" +cargo_metadata = "0.12" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index cd9363a8572..d9471d25197 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2018" [dependencies] -cargo_metadata = "0.11.1" +cargo_metadata = "0.12" if_chain = "1.0.0" itertools = "0.9" pulldown-cmark = { version = "0.8", default-features = false } From 5a13217ea9c07121e7d3cdcfb0ddd2aa52b90f12 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 16 Oct 2020 17:58:26 +0200 Subject: [PATCH 0768/1110] Assert macro args extractor as a common function in higher --- clippy_lints/src/eq_op.rs | 12 ++-- clippy_lints/src/mutable_debug_assertion.rs | 66 ++++----------------- clippy_lints/src/utils/higher.rs | 54 +++++++++++++++++ 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 9653e62cad0..3201adbf9a0 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use if_chain::if_chain; @@ -71,13 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if_chain! { if is_expn_of(stmt.span, amn).is_some(); if let StmtKind::Semi(ref matchexpr) = stmt.kind; - if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; - if let Some(ref matchheader) = matchblock.expr; - if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; - if let ExprKind::Tup(ref conditions) = headerexpr.kind; - if conditions.len() == 2; - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); + if macro_args.len() == 2; + let (lhs, rhs) = (macro_args[0], macro_args[1]); if eq_expr_value(cx, lhs, rhs); then { diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index cc635c2a202..76417aa7ed0 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,7 +1,6 @@ -use crate::utils::{is_direct_expn_of, span_lint}; -use if_chain::if_chain; +use crate::utils::{higher, is_direct_expn_of, span_lint}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp}; +use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; @@ -39,66 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { for dmn in &DEBUG_MACRO_NAMES { if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(span) = extract_call(cx, e) { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } - } -} - -//HACK(hellow554): remove this when #4694 is implemented -fn extract_call<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { - if_chain! { - if let ExprKind::Block(ref block, _) = e.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind; - then { - // debug_assert - if_chain! { - if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; - if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; - if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind; - then { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(condition); - return visitor.expr_span(); - } - } - - // debug_assert_{eq,ne} - if_chain! { - if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; - if let Some(ref matchheader) = matchblock.expr; - if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; - if let ExprKind::Tup(ref conditions) = headerexpr.kind; - if conditions.len() == 2; - then { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind { + if let Some(macro_args) = higher::extract_assert_macro_args(e) { + for arg in macro_args { let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(lhs); + visitor.visit_expr(arg); if let Some(span) = visitor.expr_span() { - return Some(span); - } - } - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(rhs); - if let Some(span) = visitor.expr_span() { - return Some(span); + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!("do not call a function with mutable arguments inside of `{}!`", dmn), + ); } } } } } } - - None } struct MutArgVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 8563b469a30..6d7c5058b4f 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -7,6 +7,7 @@ use crate::utils::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; +use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; /// Converts a hir binary operator to the corresponding `ast` type. @@ -241,3 +242,56 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option(e: &'tcx Expr<'tcx>) -> Option>> { + /// Try to match the AST for a pattern that contains a match, for example when two args are + /// compared + fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option>> { + if_chain! { + if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind; + if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind; + then { + return Some(vec![lhs, rhs]); + } + } + None + } + + if let ExprKind::Block(ref block, _) = e.kind { + if block.stmts.len() == 1 { + if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind { + // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`) + if_chain! { + if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; + if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; + if let ExprKind::Unary(UnOp::UnNot, condition) = droptmp.kind; + then { + return Some(vec![condition]); + } + } + + // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) + if_chain! { + if let ExprKind::Block(ref matchblock,_) = matchexpr.kind; + if let Some(ref matchblock_expr) = matchblock.expr; + then { + return ast_matchblock(matchblock_expr); + } + } + } + } else if let Some(matchblock_expr) = block.expr { + // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) + return ast_matchblock(&matchblock_expr); + } + } + None +} From bb0ce32423aefcb8b9eb587881973f56a6a6b0ee Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 16 Oct 2020 00:22:35 +0200 Subject: [PATCH 0769/1110] Lint unnecessary int-to-int and float-to-float casts --- clippy_lints/src/types.rs | 43 +++++++++++++++---- tests/ui/eq_op.rs | 1 + tests/ui/eq_op.stderr | 54 ++++++++++++------------ tests/ui/unnecessary_cast_fixable.fixed | 11 ++++- tests/ui/unnecessary_cast_fixable.rs | 11 ++++- tests/ui/unnecessary_cast_fixable.stderr | 32 +++++++++++++- 6 files changed, 111 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 9a948af8bfc..716d027e434 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; +use std::fmt::Display; use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; @@ -1608,18 +1609,23 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!("casting integer literal to `{}` is unnecessary", cast_to), - "try", - format!("{}_{}", n, cast_to), - Applicability::MachineApplicable, - ); + show_unnecessary_cast(cx, expr, n , cast_from, cast_to); return; } } + + match lit.node { + LitKind::Int(num, LitIntType::Unsuffixed) if cast_to.is_integral() => { + show_unnecessary_cast(cx, expr, num, cast_from, cast_to); + return; + }, + LitKind::Float(num, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + show_unnecessary_cast(cx, expr, num, cast_from, cast_to); + return; + }, + _ => (), + }; + match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { @@ -1646,6 +1652,25 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } +fn show_unnecessary_cast( + cx: &LateContext<'_>, + expr: &Expr<'_>, + num: Num, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { + let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + "try", + format!("{}_{}", num, cast_to), + Applicability::MachineApplicable, + ); +} + fn lint_numeric_casts<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31..4e09d19ea21 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -6,6 +6,7 @@ #[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] #[allow(clippy::nonminimal_bool)] #[allow(unused)] +#[allow(clippy::unnecessary_cast)] fn main() { // simple values and comparisons 1 == 1; diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078ee..ad81b35a766 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -1,5 +1,5 @@ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:11:5 + --> $DIR/eq_op.rs:12:5 | LL | 1 == 1; | ^^^^^^ @@ -7,157 +7,157 @@ LL | 1 == 1; = note: `-D clippy::eq-op` implied by `-D warnings` error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:12:5 + --> $DIR/eq_op.rs:13:5 | LL | "no" == "no"; | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:14:5 + --> $DIR/eq_op.rs:15:5 | LL | false != false; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:15:5 + --> $DIR/eq_op.rs:16:5 | LL | 1.5 < 1.5; | ^^^^^^^^^ error: equal expressions as operands to `>=` - --> $DIR/eq_op.rs:16:5 + --> $DIR/eq_op.rs:17:5 | LL | 1u64 >= 1u64; | ^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:19:5 + --> $DIR/eq_op.rs:20:5 | LL | (1 as u64) & (1 as u64); | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `^` - --> $DIR/eq_op.rs:20:5 + --> $DIR/eq_op.rs:21:5 | LL | 1 ^ ((((((1)))))); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:23:5 + --> $DIR/eq_op.rs:24:5 | LL | (-(2) < -(2)); | ^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:24:5 + --> $DIR/eq_op.rs:25:5 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:6 + --> $DIR/eq_op.rs:25:6 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:27 + --> $DIR/eq_op.rs:25:27 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:25:5 + --> $DIR/eq_op.rs:26:5 | LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:28:5 + --> $DIR/eq_op.rs:29:5 | LL | ([1] != [1]); | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:29:5 + --> $DIR/eq_op.rs:30:5 | LL | ((1, 2) != (1, 2)); | ^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:33:5 + --> $DIR/eq_op.rs:34:5 | LL | 1 + 1 == 2; | ^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:34:5 + --> $DIR/eq_op.rs:35:5 | LL | 1 - 1 == 0; | ^^^^^^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:34:5 + --> $DIR/eq_op.rs:35:5 | LL | 1 - 1 == 0; | ^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:36:5 + --> $DIR/eq_op.rs:37:5 | LL | 1 - 1; | ^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:37:5 + --> $DIR/eq_op.rs:38:5 | LL | 1 / 1; | ^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:38:5 + --> $DIR/eq_op.rs:39:5 | LL | true && true; | ^^^^^^^^^^^^ error: equal expressions as operands to `||` - --> $DIR/eq_op.rs:40:5 + --> $DIR/eq_op.rs:41:5 | LL | true || true; | ^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:46:5 + --> $DIR/eq_op.rs:47:5 | LL | a == b && b == a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:47:5 + --> $DIR/eq_op.rs:48:5 | LL | a != b && b != a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:48:5 + --> $DIR/eq_op.rs:49:5 | LL | a < b && b > a; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:49:5 + --> $DIR/eq_op.rs:50:5 | LL | a <= b && b >= a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:52:5 + --> $DIR/eq_op.rs:53:5 | LL | a == a; | ^^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:62:20 + --> $DIR/eq_op.rs:63:20 | LL | const D: u32 = A / A; | ^^^^^ diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index fb89a9fce3d..ba52fc2703f 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -12,12 +12,19 @@ fn main() { #[rustfmt::skip] let v = vec!(1); &v as &[i32]; - 1.0 as f64; - 1 as u64; 0x10 as f32; 0o10 as f32; 0b10 as f32; 0x11 as f64; 0o11 as f64; 0b11 as f64; + + 1_u32; + 16_i32; + 2_usize; + + 1.0_f64; + 0.5_f32; + + 1.0 as u16; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 4a0c8620dc1..0d2115548fd 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -12,12 +12,19 @@ fn main() { #[rustfmt::skip] let v = vec!(1); &v as &[i32]; - 1.0 as f64; - 1 as u64; 0x10 as f32; 0o10 as f32; 0b10 as f32; 0x11 as f64; 0o11 as f64; 0b11 as f64; + + 1 as u32; + 0x10 as i32; + 0b10 as usize; + + 1.0 as f64; + 0.5 as f32; + + 1.0 as u16; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 8ff1e5dea60..474e62c30d5 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -18,5 +18,35 @@ error: casting integer literal to `f64` is unnecessary LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` -error: aborting due to 3 previous errors +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:22:5 + | +LL | 1 as u32; + | ^^^^^^^^ help: try: `1_u32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:23:5 + | +LL | 0x10 as i32; + | ^^^^^^^^^^^ help: try: `16_i32` + +error: casting integer literal to `usize` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:24:5 + | +LL | 0b10 as usize; + | ^^^^^^^^^^^^^ help: try: `2_usize` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:26:5 + | +LL | 1.0 as f64; + | ^^^^^^^^^^ help: try: `1.0_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:27:5 + | +LL | 0.5 as f32; + | ^^^^^^^^^^ help: try: `0.5_f32` + +error: aborting due to 8 previous errors From 915ce3608724e6c900d1b5eb4412cac2fcace33a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 18 Oct 2020 01:11:59 +0200 Subject: [PATCH 0770/1110] manual_unwrap_or / support Result::unwrap_or --- clippy_lints/src/manual_unwrap_or.rs | 83 +++++++++++++++++++--------- src/lintlist/mod.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 44 ++++++++++++++- tests/ui/manual_unwrap_or.rs | 59 +++++++++++++++++++- tests/ui/manual_unwrap_or.stderr | 59 +++++++++++++++++++- 5 files changed, 216 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index ddb8cc25077..f3f1e31abde 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,7 +2,7 @@ use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -10,7 +10,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that reimplement `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -43,32 +43,50 @@ impl LateLintPass<'_> for ManualUnwrapOr { if in_external_macro(cx.sess(), expr.span) { return; } - lint_option_unwrap_or_case(cx, expr); + lint_manual_unwrap_or(cx, expr); } } -fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { +#[derive(Copy, Clone)] +enum Case { + Option, + Result, +} + +impl Case { + fn unwrap_fn_path(&self) -> &str { + match self { + Case::Option => "Option::unwrap_or", + Case::Result => "Result::unwrap_or", + } + } +} + +fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if let PatKind::Path(ref qpath) = arm.pat.kind { - utils::match_qpath(qpath, &utils::paths::OPTION_NONE) - } else { - false + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| + match arm.pat.kind { + PatKind::Path(ref some_qpath) => + utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE), + PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => + utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR), + _ => false, } ); - let some_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; - if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); - if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; - if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + let unwrap_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; + if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME) + || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK); + if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = unwrap_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - if !utils::usage::contains_return_break_continue_macro(none_arm.body); + if !utils::usage::contains_return_break_continue_macro(or_arm.body); then { - Some(none_arm) + Some(or_arm) } else { None } @@ -78,24 +96,35 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) { + Some(Case::Option) + } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) { + Some(Case::Result) + } else { + None + }; + if let Some(or_arm) = applicable_or_arm(match_arms); if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); + if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let reindented_or_body = + utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); + let wrap_in_parens = !matches!(scrutinee, Expr { kind: ExprKind::Call(..), .. }); + let l_paren = if wrap_in_parens { "(" } else { "" }; + let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - "this pattern reimplements `Option::unwrap_or`", + &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), "replace with", format!( - "{}.unwrap_or({})", + "{}{}{}.unwrap_or({})", + l_paren, scrutinee_snippet, - reindented_none_body, + r_paren, + reindented_or_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..b930d9aedcf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a8736f1e6ef..ceb8985d3d5 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,7 +1,7 @@ // run-rustfix #![allow(dead_code)] -fn unwrap_or() { +fn option_unwrap_or() { // int case Some(1).unwrap_or(42); @@ -65,4 +65,46 @@ fn unwrap_or() { }; } +fn result_unwrap_or() { + // int case + (Ok(1) as Result).unwrap_or(42); + + // int case reversed + (Ok(1) as Result).unwrap_or(42); + + // richer none expr + (Ok(1) as Result).unwrap_or(1 + 42); + + // multiline case + #[rustfmt::skip] + (Ok(1) as Result).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + }); + + // string case + (Ok("Bob") as Result<&str, &str>).unwrap_or("Alice"); + + // don't lint + match Ok(1) as Result { + Ok(i) => i + 2, + Err(_) => 42, + }; + match Ok(1) as Result { + Ok(i) => i, + Err(_) => return, + }; + for j in 0..4 { + match Ok(j) as Result { + Ok(i) => i, + Err(_) => continue, + }; + match Ok(j) as Result { + Ok(i) => i, + Err(_) => break, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index bede8cffc32..beca1de0ed1 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,7 +1,7 @@ // run-rustfix #![allow(dead_code)] -fn unwrap_or() { +fn option_unwrap_or() { // int case match Some(1) { Some(i) => i, @@ -80,4 +80,61 @@ fn unwrap_or() { }; } +fn result_unwrap_or() { + // int case + match Ok(1) as Result { + Ok(i) => i, + Err(_) => 42, + }; + + // int case reversed + match Ok(1) as Result { + Err(_) => 42, + Ok(i) => i, + }; + + // richer none expr + match Ok(1) as Result { + Ok(i) => i, + Err(_) => 1 + 42, + }; + + // multiline case + #[rustfmt::skip] + match Ok(1) as Result { + Ok(i) => i, + Err(_) => { + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } + }; + + // string case + match Ok("Bob") as Result<&str, &str> { + Ok(i) => i, + Err(_) => "Alice", + }; + + // don't lint + match Ok(1) as Result { + Ok(i) => i + 2, + Err(_) => 42, + }; + match Ok(1) as Result { + Ok(i) => i, + Err(_) => return, + }; + for j in 0..4 { + match Ok(j) as Result { + Ok(i) => i, + Err(_) => continue, + }; + match Ok(j) as Result { + Ok(i) => i, + Err(_) => break, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 674f2952635..5d465666caf 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -57,5 +57,62 @@ LL | | None => "Alice", LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` -error: aborting due to 5 previous errors +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:85:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:91:5 + | +LL | / match Ok(1) as Result { +LL | | Err(_) => 42, +LL | | Ok(i) => i, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:97:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => 1 + 42, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(1 + 42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:104:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => { +LL | | 42 + 42 +... | +LL | | } +LL | | }; + | |_____^ + | +help: replace with + | +LL | (Ok(1) as Result).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 +LL | }); + | + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:114:5 + | +LL | / match Ok("Bob") as Result<&str, &str> { +LL | | Ok(i) => i, +LL | | Err(_) => "Alice", +LL | | }; + | |_____^ help: replace with: `(Ok("Bob") as Result<&str, &str>).unwrap_or("Alice")` + +error: aborting due to 10 previous errors From 67bc11bd0480b5657c8c8db233e55f9b16ed664a Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Sun, 18 Oct 2020 17:01:57 -0600 Subject: [PATCH 0771/1110] Add more infomation about LintStore registration Backstory: I somehow missed the fact that I needed to register a lint pass in order for it to run, and I spent some time confused until I figured it out. So I wanted to make it clear that a missing `register_(early|late)_pass` call is a likely cause of a lint not running. --- doc/adding_lints.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index ab8ff711796..2572833b8de 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -225,6 +225,17 @@ automate everything. We will have to register our lint pass manually in the store.register_early_pass(|| box foo_functions::FooFunctions); ``` +As one may expect, there is a corresponding `register_late_pass` method +available as well. Without a call to one of `register_early_pass` or +`register_late_pass`, the lint pass in question will not be run. + +One reason that `cargo dev` does not automate this step is that multiple lints +can use the same lint pass, so registering the lint pass may already be done +when adding a new lint. Another reason that this step is not automated is that +the order that the passes are registered determines the order the passes +actually run, which in turn affects the order that any emitted lints are output +in. + [declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60 [example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints From 114cb218f3ab2a709e3017c380790dd6e407132c Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 19 Oct 2020 10:34:01 +0900 Subject: [PATCH 0772/1110] Remove an extra blank line in doc examples --- clippy_lints/src/blocks_in_if_conditions.rs | 1 - clippy_lints/src/escape.rs | 1 - clippy_lints/src/matches.rs | 2 -- clippy_lints/src/misc_early.rs | 1 - 4 files changed, 5 deletions(-) diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 1b73ced89b3..736730d4084 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -28,7 +28,6 @@ declare_clippy_lint! { /// /// ```rust /// # fn somefunc() -> bool { true }; - /// /// // Bad /// if { let x = somefunc(); x } { /* ... */ } /// diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8b022912573..1bf3b810fb5 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -29,7 +29,6 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # fn foo(bar: usize) {} - /// /// // Bad /// let x = Box::new(1); /// foo(*x); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b1a4e06d4c3..d93433c607f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -36,7 +36,6 @@ declare_clippy_lint! { /// ```rust /// # fn bar(stool: &str) {} /// # let x = Some("abc"); - /// /// // Bad /// match x { /// Some(ref foo) => bar(foo), @@ -239,7 +238,6 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); - /// /// // Bad /// match x { /// Foo::A(_) => {}, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 9cb1cfb915d..5bc45c87874 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -231,7 +231,6 @@ declare_clippy_lint! { /// ```rust /// # struct TupleStruct(u32, u32, u32); /// # let t = TupleStruct(1, 2, 3); - /// /// // Bad /// match t { /// TupleStruct(0, .., _) => (), From ec23db9496807f6c962b74fe0d6bf15be6c6d35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Jos=C3=A9=20Pereira?= Date: Sat, 3 Oct 2020 15:46:28 -0300 Subject: [PATCH 0773/1110] Add linter for a single element for loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/loops.rs | 71 +++++++++++++++++++++++++++-- src/lintlist/mod.rs | 7 +++ tests/ui/single_element_loop.fixed | 11 +++++ tests/ui/single_element_loop.rs | 10 ++++ tests/ui/single_element_loop.stderr | 19 ++++++++ 7 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/ui/single_element_loop.fixed create mode 100644 tests/ui/single_element_loop.rs create mode 100644 tests/ui/single_element_loop.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..1bf25bcd0f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1936,6 +1936,7 @@ Released 2018-09-13 [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..84ea2d5ca78 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -633,6 +633,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, &loops::SAME_ITEM_PUSH, + &loops::SINGLE_ELEMENT_LOOP, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, @@ -1363,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::SAME_ITEM_PUSH), + LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1664,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), + LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 63d7e3176b1..e50aa9ac15a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, + indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, + match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -452,6 +453,31 @@ declare_clippy_lint! { "the same item is pushed inside of a for loop" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop has a single element. + /// + /// **Why is this bad?** There is no reason to have a loop of a + /// single element. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// for item in &[item1] { + /// println!("{}", item); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item = &item1; + /// println!("{}", item); + /// ``` + pub SINGLE_ELEMENT_LOOP, + complexity, + "there is no reason to have a single element loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -469,6 +495,7 @@ declare_lint_pass!(Loops => [ MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, SAME_ITEM_PUSH, + SINGLE_ELEMENT_LOOP, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -777,6 +804,7 @@ fn check_for_loop<'tcx>( check_for_loop_arg(cx, pat, arg, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); + check_for_single_element_loop(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1866,6 +1894,43 @@ fn check_for_loop_over_map_kv<'tcx>( } } +fn check_for_single_element_loop<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; + if let PatKind::Binding(.., target, _) = pat.kind; + if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind; + if let [arg_expression] = arg_expr_list; + if let ExprKind::Path(ref list_item) = arg_expression.kind; + if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); + if let ExprKind::Block(ref block, _) = body.kind; + if !block.stmts.is_empty(); + + then { + let for_span = get_span_of_entire_for_loop(expr); + let mut block_str = snippet(cx, block.span, "..").into_owned(); + block_str.remove(0); + block_str.pop(); + + + span_lint_and_sugg( + cx, + SINGLE_ELEMENT_LOOP, + for_span, + "for loop over a single element", + "try", + format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str), + Applicability::MachineApplicable + ) + } + } +} + struct MutatePairDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, hir_id_low: Option, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..70369dc582a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2125,6 +2125,13 @@ vec![ deprecation: None, module: "single_component_path_imports", }, + Lint { + name: "single_element_loop", + group: "complexity", + desc: "there is no reason to have a single element loop", + deprecation: None, + module: "loops", + }, Lint { name: "single_match", group: "style", diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed new file mode 100644 index 00000000000..8ca068293a6 --- /dev/null +++ b/tests/ui/single_element_loop.fixed @@ -0,0 +1,11 @@ +// run-rustfix +// Tests from for_loop.rs that don't have suggestions + +#[warn(clippy::single_element_loop)] +fn main() { + let item1 = 2; + { + let item = &item1; + println!("{}", item); + } +} diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs new file mode 100644 index 00000000000..57e9336a31f --- /dev/null +++ b/tests/ui/single_element_loop.rs @@ -0,0 +1,10 @@ +// run-rustfix +// Tests from for_loop.rs that don't have suggestions + +#[warn(clippy::single_element_loop)] +fn main() { + let item1 = 2; + for item in &[item1] { + println!("{}", item); + } +} diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr new file mode 100644 index 00000000000..90be1dc3283 --- /dev/null +++ b/tests/ui/single_element_loop.stderr @@ -0,0 +1,19 @@ +error: for loop over a single element + --> $DIR/single_element_loop.rs:7:5 + | +LL | / for item in &[item1] { +LL | | println!("{}", item); +LL | | } + | |_____^ + | + = note: `-D clippy::single-element-loop` implied by `-D warnings` +help: try + | +LL | { +LL | let item = &item1; +LL | println!("{}", item); +LL | } + | + +error: aborting due to previous error + From ba1ca19c3bec20401a4cb13e5186c4c5952e94cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Jos=C3=A9=20Pereira?= Date: Sat, 3 Oct 2020 17:01:34 -0300 Subject: [PATCH 0774/1110] tests: if_same_then_else2: Ignore single_element_loop lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- tests/ui/if_same_then_else2.rs | 3 ++- tests/ui/if_same_then_else2.stderr | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 3cc21809264..8d54f75b5d1 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -3,7 +3,8 @@ clippy::blacklisted_name, clippy::collapsible_if, clippy::ifs_same_cond, - clippy::needless_return + clippy::needless_return, + clippy::single_element_loop )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index f5d087fe128..da2be6c8aa5 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:19:12 + --> $DIR/if_same_then_else2.rs:20:12 | LL | } else { | ____________^ @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:10:13 + --> $DIR/if_same_then_else2.rs:11:13 | LL | if true { | _____________^ @@ -26,7 +26,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:33:12 + --> $DIR/if_same_then_else2.rs:34:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:31:13 + --> $DIR/if_same_then_else2.rs:32:13 | LL | if true { | _____________^ @@ -45,7 +45,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:40:12 + --> $DIR/if_same_then_else2.rs:41:12 | LL | } else { | ____________^ @@ -55,7 +55,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:38:13 + --> $DIR/if_same_then_else2.rs:39:13 | LL | if true { | _____________^ @@ -64,7 +64,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:90:12 + --> $DIR/if_same_then_else2.rs:91:12 | LL | } else { | ____________^ @@ -74,7 +74,7 @@ LL | | }; | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:88:21 + --> $DIR/if_same_then_else2.rs:89:21 | LL | let _ = if true { | _____________________^ @@ -83,7 +83,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:97:12 + --> $DIR/if_same_then_else2.rs:98:12 | LL | } else { | ____________^ @@ -93,7 +93,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:95:13 + --> $DIR/if_same_then_else2.rs:96:13 | LL | if true { | _____________^ @@ -102,7 +102,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:122:12 + --> $DIR/if_same_then_else2.rs:123:12 | LL | } else { | ____________^ @@ -112,7 +112,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:119:20 + --> $DIR/if_same_then_else2.rs:120:20 | LL | } else if true { | ____________________^ From 16b5f37b5a23f475d0d94efea764c57e4572f63f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 19 Oct 2020 17:31:41 +0200 Subject: [PATCH 0775/1110] Split `eq_op` ui tests to avoid file limit error in CI --- tests/ui/eq_op.rs | 57 ---------------------- tests/ui/eq_op.stderr | 95 +----------------------------------- tests/ui/eq_op_macros.rs | 56 +++++++++++++++++++++ tests/ui/eq_op_macros.stderr | 95 ++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 151 deletions(-) create mode 100644 tests/ui/eq_op_macros.rs create mode 100644 tests/ui/eq_op_macros.stderr diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 20613ac6afe..272b0900a31 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,8 +60,6 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; - - check_assert_identical_args(); } #[rustfmt::skip] @@ -87,58 +85,3 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } - -macro_rules! assert_in_macro_def { - () => { - let a = 42; - assert_eq!(a, a); - assert_ne!(a, a); - debug_assert_eq!(a, a); - debug_assert_ne!(a, a); - }; -} - -// lint identical args in assert-like macro invocations (see #3574) -fn check_assert_identical_args() { - // lint also in macro definition - assert_in_macro_def!(); - - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); - - let my_vec = vec![1; 5]; - let mut my_iter = my_vec.iter(); - assert_ne!(my_iter.next(), my_iter.next()); -} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 21a63aec7a1..5b80e6078ee 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,98 +162,5 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:94:20 - | -LL | assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: `#[deny(clippy::eq_op)]` on by default - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:95:20 - | -LL | assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:110:16 - | -LL | assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:111:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:118:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:119:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:96:26 - | -LL | debug_assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:97:26 - | -LL | debug_assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:126:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:127:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:134:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:135:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 39 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/eq_op_macros.rs b/tests/ui/eq_op_macros.rs new file mode 100644 index 00000000000..6b5b31a1a2e --- /dev/null +++ b/tests/ui/eq_op_macros.rs @@ -0,0 +1,56 @@ +#![warn(clippy::eq_op)] + +// lint also in macro definition +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn main() { + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); + + let my_vec = vec![1; 5]; + let mut my_iter = my_vec.iter(); + assert_ne!(my_iter.next(), my_iter.next()); +} diff --git a/tests/ui/eq_op_macros.stderr b/tests/ui/eq_op_macros.stderr new file mode 100644 index 00000000000..fb9378108b9 --- /dev/null +++ b/tests/ui/eq_op_macros.stderr @@ -0,0 +1,95 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:7:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `-D clippy::eq-op` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:8:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:22:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:23:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:30:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:31:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:9:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:10:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:38:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:39:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:46:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:47:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + From 65b52d84f83586752bff2834410e131290dc0155 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 20 Oct 2020 00:38:13 +0200 Subject: [PATCH 0776/1110] needless-lifetime / multiple where clause predicates regression --- clippy_lints/src/lifetimes.rs | 6 ++++-- tests/ui/needless_lifetimes.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d7043e7bd8f..c8a5a9c9431 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -414,7 +414,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs walk_ty(&mut visitor, &pred.bounded_ty); - if !visitor.lts.is_empty() { + if !visitor.all_lts().is_empty() { return true; } // if the bounds define new lifetimes, they are fine to occur @@ -424,7 +424,9 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); + if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { + return true; + } }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index d482d466e44..6001ef37eb7 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -357,4 +357,15 @@ mod nested_elision_sites { } } +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + fn main() {} From 57bf80f77626b134faaf2cd95664403627fba0da Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 15:53:36 -0400 Subject: [PATCH 0777/1110] Add lint for holding RefCell Ref across an await --- clippy_lints/src/await_holding_refcell_ref.rs | 83 +++++++++++++++++++ clippy_lints/src/lib.rs | 4 + clippy_lints/src/utils/paths.rs | 2 + tests/ui/await_holding_refcell_ref.rs | 71 ++++++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 77 +++++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 clippy_lints/src/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.stderr diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..9a75911acbe --- /dev/null +++ b/clippy_lints/src/await_holding_refcell_ref.rs @@ -0,0 +1,83 @@ +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..69719986104 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,6 +161,7 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; +mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -510,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -906,6 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1189,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 5e769c690a6..cd9b92efe58 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -93,6 +93,8 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..6e330f47dfc --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,71 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let m = RefCell::new(100); + good(&m); + bad(&m); + bad_mut(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 00000000000..b114945504a --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,77 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:46:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:46:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:58:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:58:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + From 8727169f7243c87e3708d99e9602562370f01a1a Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 16:01:27 -0400 Subject: [PATCH 0778/1110] fmt --- tests/ui/await_holding_refcell_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 6e330f47dfc..8e30da85d14 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -64,7 +64,7 @@ fn main() { let m = RefCell::new(100); good(&m); bad(&m); - bad_mut(&m); + bad_mut(&m); also_bad(&m); not_good(&m); block_bad(&m); From 070a751d4cf350a71901f75bc99ca0e0922a3133 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 16:02:00 -0400 Subject: [PATCH 0779/1110] update_lints --- CHANGELOG.md | 1 + src/lintlist/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..287301772e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1632,6 +1632,7 @@ Released 2018-09-13 [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..21185a08d5c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -67,6 +67,13 @@ vec![ deprecation: None, module: "await_holding_lock", }, + Lint { + name: "await_holding_refcell_ref", + group: "pedantic", + desc: "Inside an async function, holding a RefCell ref while calling await", + deprecation: None, + module: "await_holding_refcell_ref", + }, Lint { name: "bad_bit_mask", group: "correctness", From 0f4abbf99a6f1ed783ea6935c83427c2aef95144 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2020 12:57:18 -0400 Subject: [PATCH 0780/1110] Better naming post copy/paste --- tests/ui/await_holding_refcell_ref.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 8e30da85d14..dd3adc494f1 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -61,11 +61,11 @@ fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { } fn main() { - let m = RefCell::new(100); - good(&m); - bad(&m); - bad_mut(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); + let rc = RefCell::new(100); + good(&rc); + bad(&rc); + bad_mut(&rc); + also_bad(&rc); + not_good(&rc); + block_bad(&rc); } From b3a427d8733a549b11f9bc88eceb31c857851411 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2020 13:29:37 -0400 Subject: [PATCH 0781/1110] Add another test case --- tests/ui/await_holding_refcell_ref.rs | 15 ++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 28 +++++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index dd3adc494f1..88841597bb6 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -39,6 +39,20 @@ async fn also_bad(x: &RefCell) -> u32 { first + second + third } +async fn less_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + async fn not_good(x: &RefCell) -> u32 { let first = baz().await; @@ -66,6 +80,7 @@ fn main() { bad(&rc); bad_mut(&rc); also_bad(&rc); + less_bad(&rc); not_good(&rc); block_bad(&rc); } diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index b114945504a..b504f045491 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -46,13 +46,31 @@ LL | | } | |_^ error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:46:13 + --> $DIR/await_holding_refcell_ref.rs:45:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:45:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:60:13 | LL | let b = x.borrow_mut(); | ^ | note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:46:9 + --> $DIR/await_holding_refcell_ref.rs:60:9 | LL | / let b = x.borrow_mut(); LL | | baz().await @@ -60,18 +78,18 @@ LL | | }; | |_____^ error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:58:13 + --> $DIR/await_holding_refcell_ref.rs:72:13 | LL | let b = x.borrow_mut(); | ^ | note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:58:9 + --> $DIR/await_holding_refcell_ref.rs:72:9 | LL | / let b = x.borrow_mut(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors From 3ed69cdb13e5953467f9d849d7ad480479ca01d6 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 12:39:20 -0400 Subject: [PATCH 0782/1110] Move existing lint into shared file --- ..._holding_lock.rs => await_holding_invalid.rs} | 0 clippy_lints/src/lib.rs | 8 ++++---- src/lintlist/mod.rs | 2 +- ..._holding_lock.rs => await_holding_invalid.rs} | 0 ..._lock.stderr => await_holding_invalid.stderr} | 16 ++++++++-------- 5 files changed, 13 insertions(+), 13 deletions(-) rename clippy_lints/src/{await_holding_lock.rs => await_holding_invalid.rs} (100%) rename tests/ui/{await_holding_lock.rs => await_holding_invalid.rs} (100%) rename tests/ui/{await_holding_lock.stderr => await_holding_invalid.stderr} (84%) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_invalid.rs similarity index 100% rename from clippy_lints/src/await_holding_lock.rs rename to clippy_lints/src/await_holding_invalid.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 69719986104..47cc0c90322 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -160,7 +160,7 @@ mod assign_ops; mod async_yields_async; mod atomic_ordering; mod attrs; -mod await_holding_lock; +mod await_holding_invalid; mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; @@ -510,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::MISMATCHED_TARGET_OS, &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, - &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_invalid::AWAIT_HOLDING_LOCK, &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, @@ -907,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -1191,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 21185a08d5c..63e9220ccd5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -65,7 +65,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, - module: "await_holding_lock", + module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_invalid.rs similarity index 100% rename from tests/ui/await_holding_lock.rs rename to tests/ui/await_holding_invalid.rs diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_invalid.stderr similarity index 84% rename from tests/ui/await_holding_lock.stderr rename to tests/ui/await_holding_invalid.stderr index 21bf49d16f0..315d5731b96 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_invalid.stderr @@ -1,12 +1,12 @@ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:7:9 + --> $DIR/await_holding_invalid.rs:7:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = note: `-D clippy::await-holding-lock` implied by `-D warnings` note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:7:5 + --> $DIR/await_holding_invalid.rs:7:5 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -14,13 +14,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:28:9 + --> $DIR/await_holding_invalid.rs:28:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:28:5 + --> $DIR/await_holding_invalid.rs:28:5 | LL | / let guard = x.lock().unwrap(); LL | | @@ -32,13 +32,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:41:13 + --> $DIR/await_holding_invalid.rs:41:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:41:9 + --> $DIR/await_holding_invalid.rs:41:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -46,13 +46,13 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:53:13 + --> $DIR/await_holding_invalid.rs:53:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:53:9 + --> $DIR/await_holding_invalid.rs:53:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await From ee20ebadafc9b2e4995015097e376c0a866d84af Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:15:45 -0400 Subject: [PATCH 0783/1110] Move refcell lint into shared module --- clippy_lints/src/await_holding_invalid.rs | 80 ++++++++++++- clippy_lints/src/await_holding_refcell_ref.rs | 83 ------------- clippy_lints/src/lib.rs | 7 +- src/lintlist/mod.rs | 2 +- tests/ui/await_holding_invalid.rs | 106 +++++++++++++++-- tests/ui/await_holding_invalid.stderr | 111 ++++++++++++++++-- tests/ui/await_holding_refcell_ref.rs | 86 -------------- tests/ui/await_holding_refcell_ref.stderr | 95 --------------- 8 files changed, 277 insertions(+), 293 deletions(-) delete mode 100644 clippy_lints/src/await_holding_refcell_ref.rs delete mode 100644 tests/ui/await_holding_refcell_ref.rs delete mode 100644 tests/ui/await_holding_refcell_ref.stderr diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 367534499fd..2000bdae363 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -60,12 +60,12 @@ impl LateLintPass<'_> for AwaitHoldingLock { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { if is_mutex_guard(cx, adt.did) { @@ -90,3 +90,79 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) } + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs deleted file mode 100644 index 9a75911acbe..00000000000 --- a/clippy_lints/src/await_holding_refcell_ref.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; -use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for calls to await while holding a - /// `RefCell` `Ref` or `RefMut`. - /// - /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access - /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point - /// risks panics from a mutable ref shared while other refs are outstanding. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// let b = x.borrow_mut()(); - /// *ref += 1; - /// bar.await; - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// { - /// let b = x.borrow_mut(); - /// *ref += 1; - /// } - /// bar.await; - /// } - /// ``` - pub AWAIT_HOLDING_REFCELL_REF, - pedantic, - "Inside an async function, holding a RefCell ref while calling await" -} - -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); - -impl LateLintPass<'_> for AwaitHoldingRefCellRef { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_refcell_ref(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this ref is held through", - ); - } - } - } -} - -fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 47cc0c90322..0c5baf96970 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,7 +161,6 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_invalid; -mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -511,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_invalid::AWAIT_HOLDING_LOCK, - &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, + &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1192,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 63e9220ccd5..c955f37b83a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -72,7 +72,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, - module: "await_holding_refcell_ref", + module: "await_holding_invalid", }, Lint { name: "bad_bit_mask", diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs index 0458950edee..45aa3e64292 100644 --- a/tests/ui/await_holding_invalid.rs +++ b/tests/ui/await_holding_invalid.rs @@ -1,14 +1,15 @@ // edition:2018 -#![warn(clippy::await_holding_lock)] +#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] +use std::cell::RefCell; use std::sync::Mutex; -async fn bad(x: &Mutex) -> u32 { +async fn bad_lock(x: &Mutex) -> u32 { let guard = x.lock().unwrap(); baz().await } -async fn good(x: &Mutex) -> u32 { +async fn good_lock(x: &Mutex) -> u32 { { let guard = x.lock().unwrap(); let y = *guard + 1; @@ -22,7 +23,7 @@ async fn baz() -> u32 { 42 } -async fn also_bad(x: &Mutex) -> u32 { +async fn also_bad_lock(x: &Mutex) -> u32 { let first = baz().await; let guard = x.lock().unwrap(); @@ -34,7 +35,7 @@ async fn also_bad(x: &Mutex) -> u32 { first + second + third } -async fn not_good(x: &Mutex) -> u32 { +async fn not_good_lock(x: &Mutex) -> u32 { let first = baz().await; let second = { @@ -48,18 +49,97 @@ async fn not_good(x: &Mutex) -> u32 { } #[allow(clippy::manual_async_fn)] -fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { +fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); baz().await } } -fn main() { - let m = Mutex::new(100); - good(&m); - bad(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); +async fn bad_refcell(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut_refcell(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good_refcell(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn also_bad_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn less_bad_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + +async fn not_good_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad_refcell(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + { + let m = Mutex::new(100); + good_lock(&m); + bad_lock(&m); + also_bad_lock(&m); + not_good_lock(&m); + block_bad_lock(&m); + } + { + let rc = RefCell::new(100); + good_refcell(&rc); + bad_refcell(&rc); + bad_mut_refcell(&rc); + also_bad_refcell(&rc); + less_bad_refcell(&rc); + not_good_refcell(&rc); + block_bad_refcell(&rc); + } } diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr index 315d5731b96..c8d49820c02 100644 --- a/tests/ui/await_holding_invalid.stderr +++ b/tests/ui/await_holding_invalid.stderr @@ -1,12 +1,12 @@ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:7:9 + --> $DIR/await_holding_invalid.rs:8:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = note: `-D clippy::await-holding-lock` implied by `-D warnings` note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:7:5 + --> $DIR/await_holding_invalid.rs:8:5 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -14,13 +14,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:28:9 + --> $DIR/await_holding_invalid.rs:29:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:28:5 + --> $DIR/await_holding_invalid.rs:29:5 | LL | / let guard = x.lock().unwrap(); LL | | @@ -32,13 +32,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:41:13 + --> $DIR/await_holding_invalid.rs:42:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:41:9 + --> $DIR/await_holding_invalid.rs:42:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -46,18 +46,111 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:53:13 + --> $DIR/await_holding_invalid.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:53:9 + --> $DIR/await_holding_invalid.rs:54:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 4 previous errors +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:60:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:60:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:65:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:65:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:82:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:82:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:94:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:94:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:109:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:109:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:121:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:121:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs deleted file mode 100644 index 88841597bb6..00000000000 --- a/tests/ui/await_holding_refcell_ref.rs +++ /dev/null @@ -1,86 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; - -async fn bad(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - let rc = RefCell::new(100); - good(&rc); - bad(&rc); - bad_mut(&rc); - also_bad(&rc); - less_bad(&rc); - not_good(&rc); - block_bad(&rc); -} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr deleted file mode 100644 index b504f045491..00000000000 --- a/tests/ui/await_holding_refcell_ref.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:7:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:7:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:12:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:12:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:33:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:33:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:45:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:45:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:60:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:60:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:72:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:72:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 6 previous errors - From d8c6bce4407b1c99ed961f75a093ffe767818069 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:20:01 -0400 Subject: [PATCH 0784/1110] Convert the await holding lints to correctness --- clippy_lints/src/await_holding_invalid.rs | 4 ++-- clippy_lints/src/lib.rs | 6 ++++-- src/lintlist/mod.rs | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 2000bdae363..1d201ff095b 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_LOCK, - pedantic, + correctness, "Inside an async function, holding a MutexGuard while calling await" } @@ -126,7 +126,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_REFCELL_REF, - pedantic, + correctness, "Inside an async function, holding a RefCell ref while calling await" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c5baf96970..c627df20976 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1190,8 +1190,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), @@ -1290,6 +1288,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), @@ -1736,6 +1736,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&booleans::LOGIC_BUG), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c955f37b83a..d9e019e2b61 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -62,14 +62,14 @@ vec![ }, Lint { name: "await_holding_lock", - group: "pedantic", + group: "correctness", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", - group: "pedantic", + group: "correctness", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, module: "await_holding_invalid", From 86f2b29d2ff33862264e2e6dfdc7cc20ad054ad1 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 19 Oct 2020 12:06:43 -0400 Subject: [PATCH 0785/1110] Merge lints into one pass --- clippy_lints/src/await_holding_invalid.rs | 69 ++++++++--------------- clippy_lints/src/lib.rs | 3 +- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 1d201ff095b..fcebb54c6c2 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -49,48 +49,6 @@ declare_clippy_lint! { "Inside an async function, holding a MutexGuard while calling await" } -declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); - -impl LateLintPass<'_> for AwaitHoldingLock { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this lock is held through", - ); - } - } - } -} - -fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) -} - declare_clippy_lint! { /// **What it does:** Checks for calls to await while holding a /// `RefCell` `Ref` or `RefMut`. @@ -130,9 +88,9 @@ declare_clippy_lint! { "Inside an async function, holding a RefCell ref while calling await" } -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); +declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); -impl LateLintPass<'_> for AwaitHoldingRefCellRef { +impl LateLintPass<'_> for AwaitHolding { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -141,14 +99,24 @@ impl LateLintPass<'_> for AwaitHoldingRefCellRef { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this lock is held through", + ); + } if is_refcell_ref(cx, adt.did) { span_lint_and_note( cx, @@ -163,6 +131,15 @@ fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInte } } +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) +} + fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c627df20976..6d00e313ce1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -906,8 +906,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); From 4d3322525d9b65ce4c6fc183bc1cd3cfc9477300 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 19 Oct 2020 12:07:04 -0400 Subject: [PATCH 0786/1110] Separate tests for each lint --- tests/ui/await_holding_invalid.rs | 145 -------------------- tests/ui/await_holding_invalid.stderr | 156 ---------------------- tests/ui/await_holding_lock.rs | 65 +++++++++ tests/ui/await_holding_lock.stderr | 63 +++++++++ tests/ui/await_holding_refcell_ref.rs | 86 ++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 95 +++++++++++++ 6 files changed, 309 insertions(+), 301 deletions(-) delete mode 100644 tests/ui/await_holding_invalid.rs delete mode 100644 tests/ui/await_holding_invalid.stderr create mode 100644 tests/ui/await_holding_lock.rs create mode 100644 tests/ui/await_holding_lock.stderr create mode 100644 tests/ui/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.stderr diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs deleted file mode 100644 index 45aa3e64292..00000000000 --- a/tests/ui/await_holding_invalid.rs +++ /dev/null @@ -1,145 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; -use std::sync::Mutex; - -async fn bad_lock(x: &Mutex) -> u32 { - let guard = x.lock().unwrap(); - baz().await -} - -async fn good_lock(x: &Mutex) -> u32 { - { - let guard = x.lock().unwrap(); - let y = *guard + 1; - } - baz().await; - let guard = x.lock().unwrap(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad_lock(x: &Mutex) -> u32 { - let first = baz().await; - - let guard = x.lock().unwrap(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn not_good_lock(x: &Mutex) -> u32 { - let first = baz().await; - - let second = { - let guard = x.lock().unwrap(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { - async move { - let guard = x.lock().unwrap(); - baz().await - } -} - -async fn bad_refcell(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut_refcell(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good_refcell(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn also_bad_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad_refcell(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - { - let m = Mutex::new(100); - good_lock(&m); - bad_lock(&m); - also_bad_lock(&m); - not_good_lock(&m); - block_bad_lock(&m); - } - { - let rc = RefCell::new(100); - good_refcell(&rc); - bad_refcell(&rc); - bad_mut_refcell(&rc); - also_bad_refcell(&rc); - less_bad_refcell(&rc); - not_good_refcell(&rc); - block_bad_refcell(&rc); - } -} diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr deleted file mode 100644 index c8d49820c02..00000000000 --- a/tests/ui/await_holding_invalid.stderr +++ /dev/null @@ -1,156 +0,0 @@ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:8:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | - = note: `-D clippy::await-holding-lock` implied by `-D warnings` -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:8:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:29:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:29:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:42:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:42:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:54:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:54:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:60:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:60:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:65:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:65:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:82:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:82:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:94:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:94:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:109:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:109:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:121:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:121:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 10 previous errors - diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs new file mode 100644 index 00000000000..0458950edee --- /dev/null +++ b/tests/ui/await_holding_lock.rs @@ -0,0 +1,65 @@ +// edition:2018 +#![warn(clippy::await_holding_lock)] + +use std::sync::Mutex; + +async fn bad(x: &Mutex) -> u32 { + let guard = x.lock().unwrap(); + baz().await +} + +async fn good(x: &Mutex) -> u32 { + { + let guard = x.lock().unwrap(); + let y = *guard + 1; + } + baz().await; + let guard = x.lock().unwrap(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &Mutex) -> u32 { + let first = baz().await; + + let guard = x.lock().unwrap(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &Mutex) -> u32 { + let first = baz().await; + + let second = { + let guard = x.lock().unwrap(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { + async move { + let guard = x.lock().unwrap(); + baz().await + } +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr new file mode 100644 index 00000000000..21bf49d16f0 --- /dev/null +++ b/tests/ui/await_holding_lock.stderr @@ -0,0 +1,63 @@ +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:7:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | + = note: `-D clippy::await-holding-lock` implied by `-D warnings` +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:7:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:28:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:28:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:41:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:41:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:53:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:53:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..88841597bb6 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,86 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn less_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let rc = RefCell::new(100); + good(&rc); + bad(&rc); + bad_mut(&rc); + also_bad(&rc); + less_bad(&rc); + not_good(&rc); + block_bad(&rc); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 00000000000..b504f045491 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,95 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:45:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:45:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:60:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:60:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:72:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:72:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 6 previous errors + From 496dbb6fe80c8102e36c5927c117fb085b199ac7 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 22 Oct 2020 08:51:03 +0900 Subject: [PATCH 0787/1110] Fix links to labeled issues --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 100c9edb367..6494695606c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -316,8 +316,8 @@ If you have @bors permissions, you can find an overview of the available commands [here][homu_instructions]. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash%20%3Aboom%3A -[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A +[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash +[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug [homu]: https://github.com/rust-lang/homu [homu_instructions]: https://buildbot2.rust-lang.org/homu/ [homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy From 4a4f998c39b1b7e06c34a5fc8ed90e8752142e20 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Fri, 16 Oct 2020 11:12:37 -0400 Subject: [PATCH 0788/1110] Add new lint for undropped ManuallyDrop values --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/undropped_manually_drops.rs | 49 ++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/undropped_manually_drops.rs | 18 +++++++ 5 files changed, 79 insertions(+) create mode 100644 clippy_lints/src/undropped_manually_drops.rs create mode 100644 tests/ui/undropped_manually_drops.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..697e6166fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1979,6 +1979,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..49ff8ad366e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -314,6 +314,7 @@ mod transmuting_null; mod trivially_copy_pass_by_ref; mod try_err; mod types; +mod undropped_manually_drops; mod unicode; mod unit_return_expecting_ord; mod unnamed_address; @@ -862,6 +863,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, @@ -1137,6 +1139,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1790,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), + LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs new file mode 100644 index 00000000000..48a050777b7 --- /dev/null +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -0,0 +1,49 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; +use crate::utils::{match_function_call, is_type_lang_item, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. + /// + /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// unsafe { + /// std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + /// } + /// ``` + pub UNDROPPED_MANUALLY_DROPS, + correctness, + "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" +} + +declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); + +impl LateLintPass<'tcx> for UndroppedManuallyDrops { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) { + let ty = cx.typeck_results().expr_ty(&args[0]); + if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { + span_lint_and_help( + cx, + UNDROPPED_MANUALLY_DROPS, + expr.span, + "the inner value of this ManuallyDrop will not be dropped", + None, + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop" + ); + } + } + } +} \ No newline at end of file diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..25a69e78c8f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2412,6 +2412,13 @@ vec![ deprecation: None, module: "trait_bounds", }, + Lint { + name: "undropped_manually_drops", + group: "correctness", + desc: "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value", + deprecation: None, + module: "undropped_manually_drops", + }, Lint { name: "unicode_not_nfc", group: "pedantic", diff --git a/tests/ui/undropped_manually_drops.rs b/tests/ui/undropped_manually_drops.rs new file mode 100644 index 00000000000..bea62e1751e --- /dev/null +++ b/tests/ui/undropped_manually_drops.rs @@ -0,0 +1,18 @@ +#![warn(clippy::undropped_manually_drops)] + +struct S; + +fn main() { + let f = drop; + let manual = std::mem::ManuallyDrop::new(S); + + // These lines will not drop `S` + drop(std::mem::ManuallyDrop::new(S)); + f(manual); + + // These lines will + unsafe { + std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(manual); + } +} From e70817e712fd4d4e930ead0d587031e2b4a97a2e Mon Sep 17 00:00:00 2001 From: cgm616 Date: Fri, 16 Oct 2020 16:20:03 -0400 Subject: [PATCH 0789/1110] Update tests and add known problems to docs --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/undropped_manually_drops.rs | 15 ++++++------- tests/ui/undropped_manually_drops.rs | 22 +++++++++++++------- tests/ui/undropped_manually_drops.stderr | 19 +++++++++++++++++ 4 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 tests/ui/undropped_manually_drops.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 49ff8ad366e..97e7cfd1bb2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1524,6 +1524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), + LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 48a050777b7..5443f1601fc 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -1,14 +1,15 @@ -use rustc_lint::{LateLintPass, LateContext}; +use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help}; +use rustc_hir::{lang_items, Expr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; -use crate::utils::{match_function_call, is_type_lang_item, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. /// /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`. /// - /// **Known problems:** None. + /// **Known problems:** Does not catch cases if the user binds `std::mem::drop` + /// to a different name and calls it that way. /// /// **Example:** /// @@ -20,7 +21,7 @@ declare_clippy_lint! { /// ```rust /// struct S; /// unsafe { - /// std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); /// } /// ``` pub UNDROPPED_MANUALLY_DROPS, @@ -41,9 +42,9 @@ impl LateLintPass<'tcx> for UndroppedManuallyDrops { expr.span, "the inner value of this ManuallyDrop will not be dropped", None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop" + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", ); } } } -} \ No newline at end of file +} diff --git a/tests/ui/undropped_manually_drops.rs b/tests/ui/undropped_manually_drops.rs index bea62e1751e..f4cfc92e1cd 100644 --- a/tests/ui/undropped_manually_drops.rs +++ b/tests/ui/undropped_manually_drops.rs @@ -3,16 +3,24 @@ struct S; fn main() { - let f = drop; - let manual = std::mem::ManuallyDrop::new(S); + let f = std::mem::drop; + let g = std::mem::ManuallyDrop::drop; + let mut manual1 = std::mem::ManuallyDrop::new(S); + let mut manual2 = std::mem::ManuallyDrop::new(S); + let mut manual3 = std::mem::ManuallyDrop::new(S); + let mut manual4 = std::mem::ManuallyDrop::new(S); - // These lines will not drop `S` + // These lines will not drop `S` and should be linted drop(std::mem::ManuallyDrop::new(S)); - f(manual); + drop(manual1); - // These lines will + // FIXME: this line is not linted, though it should be + f(manual2); + + // These lines will drop `S` and should be okay. unsafe { - std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); - std::mem::ManuallyDrop::drop(manual); + std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(&mut manual3); + g(&mut manual4); } } diff --git a/tests/ui/undropped_manually_drops.stderr b/tests/ui/undropped_manually_drops.stderr new file mode 100644 index 00000000000..2ac0fe98697 --- /dev/null +++ b/tests/ui/undropped_manually_drops.stderr @@ -0,0 +1,19 @@ +error: the inner value of this ManuallyDrop will not be dropped + --> $DIR/undropped_manually_drops.rs:14:5 + | +LL | drop(std::mem::ManuallyDrop::new(S)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::undropped-manually-drops` implied by `-D warnings` + = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop + +error: the inner value of this ManuallyDrop will not be dropped + --> $DIR/undropped_manually_drops.rs:15:5 + | +LL | drop(manual1); + | ^^^^^^^^^^^^^ + | + = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop + +error: aborting due to 2 previous errors + From c693de350aff4a3930e66bccf65caf79b390dca2 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 15 Oct 2020 22:05:51 +0200 Subject: [PATCH 0790/1110] New lint: manual-range-contains --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/ranges.rs | 216 ++++++++++++++++++++++++++++----- src/lintlist/mod.rs | 7 ++ tests/ui/range_contains.fixed | 41 +++++++ tests/ui/range_contains.rs | 41 +++++++ tests/ui/range_contains.stderr | 76 ++++++++++++ 7 files changed, 354 insertions(+), 31 deletions(-) create mode 100644 tests/ui/range_contains.fixed create mode 100644 tests/ui/range_contains.rs create mode 100644 tests/ui/range_contains.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..c9f0406a806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1793,6 +1793,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..cc50655cb00 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -785,6 +785,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ptr_eq::PTR_EQ, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &question_mark::QUESTION_MARK, + &ranges::MANUAL_RANGE_CONTAINS, &ranges::RANGE_MINUS_ONE, &ranges::RANGE_PLUS_ONE, &ranges::RANGE_ZIP_WITH_LEN, @@ -1469,6 +1470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), + LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), @@ -1624,6 +1626,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::PTR_ARG), LintId::of(&ptr_eq::PTR_EQ), LintId::of(&question_mark::QUESTION_MARK), + LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index cc492917b9d..de54711d851 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,15 +2,19 @@ use crate::consts::{constant, Constant}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::Ident; use std::cmp::Ordering; use crate::utils::sugg::Sugg; -use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; +use crate::utils::{ + get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, + span_lint, span_lint_and_sugg, span_lint_and_then, +}; use crate::utils::{higher, SpanlessEq}; declare_clippy_lint! { @@ -128,43 +132,51 @@ declare_clippy_lint! { "reversing the limits of range expressions, resulting in empty ranges" } +declare_clippy_lint! { + /// **What it does:** Checks for expressions like `x >= 3 && x < 8` that could + /// be more readably expressed as `(3..8).contains(x)`. + /// + /// **Why is this bad?** `contains` expresses the intent better and has less + /// failure modes (such as fencepost errors or using `||` instead of `&&`). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // given + /// let x = 6; + /// + /// assert!(x >= 3 && x < 8); + /// ``` + /// Use instead: + /// ```rust + ///# let x = 6; + /// assert!((3..8).contains(&x)); + /// ``` + pub MANUAL_RANGE_CONTAINS, + style, + "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" +} + declare_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, REVERSED_EMPTY_RANGES, + MANUAL_RANGE_CONTAINS, ]); impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - let name = path.ident.as_str(); - if name == "zip" && args.len() == 2 { - let iter = &args[0].kind; - let zip_arg = &args[1]; - if_chain! { - // `.iter()` call - if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter; - if iter_path.ident.name == sym!(iter); - // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); - if is_integer_const(cx, start, 0); - // `.len()` call - if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; - if len_path.ident.name == sym!(len) && len_args.len() == 1; - // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; - if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - expr.span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, iter_args[0].span, "_"))); - } - } - } + match expr.kind { + ExprKind::MethodCall(ref path, _, ref args, _) => { + check_range_zip_with_len(cx, path, args, expr.span); + }, + ExprKind::Binary(ref op, ref l, ref r) => { + check_possible_range_contains(cx, op.node, l, r, expr.span); + }, + _ => {}, } check_exclusive_range_plus_one(cx, expr); @@ -173,6 +185,148 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { } } +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { + let combine_and = match op { + BinOpKind::And | BinOpKind::BitAnd => true, + BinOpKind::Or | BinOpKind::BitOr => false, + _ => return, + }; + // value, name, order (higher/lower), inclusiveness + if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) = + (check_range_bounds(cx, l), check_range_bounds(cx, r)) + { + // we only lint comparisons on the same name and with different + // direction + if lname != rname || lord == rord { + return; + } + let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); + if combine_and && ord == Some(rord) { + // order lower bound and upper bound + let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less { + (lval_span, rval_span, linc, rinc) + } else { + (rval_span, lval_span, rinc, linc) + }; + // we only lint inclusive lower bounds + if !l_inc { + return; + } + let (range_type, range_op) = if u_inc { + ("RangeInclusive", "..=") + } else { + ("Range", "..") + }; + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); + let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_RANGE_CONTAINS, + span, + &format!("manual `{}::contains` implementation", range_type), + "use", + format!("({}{}{}).contains(&{})", lo, range_op, hi, name), + applicability, + ); + } else if !combine_and && ord == Some(lord) { + // `!_.contains(_)` + // order lower bound and upper bound + let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less { + (lval_span, rval_span, linc, rinc) + } else { + (rval_span, lval_span, rinc, linc) + }; + if l_inc { + return; + } + let (range_type, range_op) = if u_inc { + ("Range", "..") + } else { + ("RangeInclusive", "..=") + }; + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); + let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_RANGE_CONTAINS, + span, + &format!("manual `!{}::contains` implementation", range_type), + "use", + format!("!({}{}{}).contains(&{})", lo, range_op, hi, name), + applicability, + ); + } + } +} + +fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> { + if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind { + let (inclusive, ordering) = match op.node { + BinOpKind::Gt => (false, Ordering::Greater), + BinOpKind::Ge => (true, Ordering::Greater), + BinOpKind::Lt => (false, Ordering::Less), + BinOpKind::Le => (true, Ordering::Less), + _ => return None, + }; + if let Some(id) = match_ident(l) { + if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { + return Some((c, id, l.span, r.span, ordering, inclusive)); + } + } else if let Some(id) = match_ident(r) { + if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { + return Some((c, id, r.span, l.span, ordering.reverse(), inclusive)); + } + } + } + None +} + +fn match_ident(e: &Expr<'_>) -> Option { + if let ExprKind::Path(ref qpath) = e.kind { + if let Some(seg) = single_segment_path(qpath) { + if seg.args.is_none() { + return Some(seg.ident); + } + } + } + None +} + +fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) { + let name = path.ident.as_str(); + if name == "zip" && args.len() == 2 { + let iter = &args[0].kind; + let zip_arg = &args[1]; + if_chain! { + // `.iter()` call + if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; + if iter_path.ident.name == sym!(iter); + // range expression in `.zip()` call: `0..x.len()` + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); + if is_integer_const(cx, start, 0); + // `.len()` call + if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; + if len_path.ident.name == sym!(len) && len_args.len() == 1; + // `.iter()` and `.len()` called on same `Path` + if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; + if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + then { + span_lint(cx, + RANGE_ZIP_WITH_LEN, + span, + &format!("it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, iter_args[0].span, "_")) + ); + } + } + } +} + // exclusive range plus one: `x..(y+1)` fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..7bb68acc062 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1159,6 +1159,13 @@ vec![ deprecation: None, module: "manual_non_exhaustive", }, + Lint { + name: "manual_range_contains", + group: "style", + desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`", + deprecation: None, + module: "ranges", + }, Lint { name: "manual_saturating_arithmetic", group: "style", diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed new file mode 100644 index 00000000000..632a6592a28 --- /dev/null +++ b/tests/ui/range_contains.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#[warn(clippy::manual_range_contains)] +#[allow(unused)] +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +fn main() { + let x = 9_u32; + + // order shouldn't matter + (8..12).contains(&x); + (21..42).contains(&x); + (1..100).contains(&x); + + // also with inclusive ranges + (9..=99).contains(&x); + (1..=33).contains(&x); + (1..=999).contains(&x); + + // and the outside + !(8..12).contains(&x); + !(21..42).contains(&x); + !(1..100).contains(&x); + + // also with the outside of inclusive ranges + !(9..=99).contains(&x); + !(1..=33).contains(&x); + !(1..=999).contains(&x); + + // not a range.contains + x > 8 && x < 12; // lower bound not inclusive + x < 8 && x <= 12; // same direction + x >= 12 && 12 >= x; // same bounds + x < 8 && x > 12; // wrong direction + + x <= 8 || x >= 12; + x >= 8 || x >= 12; + x < 12 || 12 < x; + x >= 8 || x <= 12; +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs new file mode 100644 index 00000000000..6af0d034ef6 --- /dev/null +++ b/tests/ui/range_contains.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#[warn(clippy::manual_range_contains)] +#[allow(unused)] +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +fn main() { + let x = 9_u32; + + // order shouldn't matter + x >= 8 && x < 12; + x < 42 && x >= 21; + 100 > x && 1 <= x; + + // also with inclusive ranges + x >= 9 && x <= 99; + x <= 33 && x >= 1; + 999 >= x && 1 <= x; + + // and the outside + x < 8 || x >= 12; + x >= 42 || x < 21; + 100 <= x || 1 > x; + + // also with the outside of inclusive ranges + x < 9 || x > 99; + x > 33 || x < 1; + 999 < x || 1 > x; + + // not a range.contains + x > 8 && x < 12; // lower bound not inclusive + x < 8 && x <= 12; // same direction + x >= 12 && 12 >= x; // same bounds + x < 8 && x > 12; // wrong direction + + x <= 8 || x >= 12; + x >= 8 || x >= 12; + x < 12 || 12 < x; + x >= 8 || x <= 12; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr new file mode 100644 index 00000000000..69b009eafc3 --- /dev/null +++ b/tests/ui/range_contains.stderr @@ -0,0 +1,76 @@ +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:12:5 + | +LL | x >= 8 && x < 12; + | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` + | + = note: `-D clippy::manual-range-contains` implied by `-D warnings` + +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:13:5 + | +LL | x < 42 && x >= 21; + | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` + +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:14:5 + | +LL | 100 > x && 1 <= x; + | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:17:5 + | +LL | x >= 9 && x <= 99; + | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:18:5 + | +LL | x <= 33 && x >= 1; + | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:19:5 + | +LL | 999 >= x && 1 <= x; + | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:22:5 + | +LL | x < 8 || x >= 12; + | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:23:5 + | +LL | x >= 42 || x < 21; + | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:24:5 + | +LL | 100 <= x || 1 > x; + | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:27:5 + | +LL | x < 9 || x > 99; + | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:28:5 + | +LL | x > 33 || x < 1; + | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:29:5 + | +LL | 999 < x || 1 > x; + | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` + +error: aborting due to 12 previous errors + From f2da0c701edef601b16b512b3a244977bf4b3afe Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 22 Oct 2020 22:46:10 +0200 Subject: [PATCH 0791/1110] manual-unwrap-or / pr remarks --- tests/ui/manual_unwrap_or.fixed | 31 ++++++++++++----- tests/ui/manual_unwrap_or.rs | 34 ++++++++++++++----- tests/ui/manual_unwrap_or.stderr | 57 ++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 40 deletions(-) diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index ceb8985d3d5..c784de0f604 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,5 +1,6 @@ // run-rustfix #![allow(dead_code)] +#![allow(unused_variables)] fn option_unwrap_or() { // int case @@ -67,44 +68,58 @@ fn option_unwrap_or() { fn result_unwrap_or() { // int case + Ok::(1).unwrap_or(42); + + // int case, suggestion must surround with parenthesis (Ok(1) as Result).unwrap_or(42); // int case reversed - (Ok(1) as Result).unwrap_or(42); + Ok::(1).unwrap_or(42); // richer none expr - (Ok(1) as Result).unwrap_or(1 + 42); + Ok::(1).unwrap_or(1 + 42); // multiline case #[rustfmt::skip] - (Ok(1) as Result).unwrap_or({ + Ok::(1).unwrap_or({ 42 + 42 + 42 + 42 + 42 + 42 + 42 + 42 }); // string case - (Ok("Bob") as Result<&str, &str>).unwrap_or("Alice"); + Ok::<&str, &str>("Bob").unwrap_or("Alice"); // don't lint - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i + 2, Err(_) => 42, }; - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => return, }; for j in 0..4 { - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => continue, }; - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => break, }; } + + // don't lint, Err value is used + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => s, + }; + // could lint, but unused_variables takes care of it + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => "Bob", + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index beca1de0ed1..df5f237c3fb 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,5 +1,6 @@ // run-rustfix #![allow(dead_code)] +#![allow(unused_variables)] fn option_unwrap_or() { // int case @@ -82,26 +83,32 @@ fn option_unwrap_or() { fn result_unwrap_or() { // int case + match Ok::(1) { + Ok(i) => i, + Err(_) => 42, + }; + + // int case, suggestion must surround with parenthesis match Ok(1) as Result { Ok(i) => i, Err(_) => 42, }; // int case reversed - match Ok(1) as Result { + match Ok::(1) { Err(_) => 42, Ok(i) => i, }; // richer none expr - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => 1 + 42, }; // multiline case #[rustfmt::skip] - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => { 42 + 42 @@ -111,30 +118,41 @@ fn result_unwrap_or() { }; // string case - match Ok("Bob") as Result<&str, &str> { + match Ok::<&str, &str>("Bob") { Ok(i) => i, Err(_) => "Alice", }; // don't lint - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i + 2, Err(_) => 42, }; - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => return, }; for j in 0..4 { - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => continue, }; - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => break, }; } + + // don't lint, Err value is used + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => s, + }; + // could lint, but unused_variables takes care of it + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => "Bob", + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 5d465666caf..5bc01bf4e68 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -1,5 +1,5 @@ error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:6:5 + --> $DIR/manual_unwrap_or.rs:7:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-unwrap-or` implied by `-D warnings` error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:12:5 + --> $DIR/manual_unwrap_or.rs:13:5 | LL | / match Some(1) { LL | | None => 42, @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:18:5 + --> $DIR/manual_unwrap_or.rs:19:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:25:5 + --> $DIR/manual_unwrap_or.rs:26:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -49,7 +49,7 @@ LL | }); | error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:35:5 + --> $DIR/manual_unwrap_or.rs:36:5 | LL | / match Some("Bob") { LL | | Some(i) => i, @@ -58,7 +58,16 @@ LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:85:5 + --> $DIR/manual_unwrap_or.rs:86:5 + | +LL | / match Ok::(1) { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:92:5 | LL | / match Ok(1) as Result { LL | | Ok(i) => i, @@ -67,27 +76,27 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:91:5 + --> $DIR/manual_unwrap_or.rs:98:5 | -LL | / match Ok(1) as Result { +LL | / match Ok::(1) { LL | | Err(_) => 42, LL | | Ok(i) => i, LL | | }; - | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` - -error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:97:5 - | -LL | / match Ok(1) as Result { -LL | | Ok(i) => i, -LL | | Err(_) => 1 + 42, -LL | | }; - | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(1 + 42)` + | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` --> $DIR/manual_unwrap_or.rs:104:5 | -LL | / match Ok(1) as Result { +LL | / match Ok::(1) { +LL | | Ok(i) => i, +LL | | Err(_) => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:111:5 + | +LL | / match Ok::(1) { LL | | Ok(i) => i, LL | | Err(_) => { LL | | 42 + 42 @@ -98,7 +107,7 @@ LL | | }; | help: replace with | -LL | (Ok(1) as Result).unwrap_or({ +LL | Ok::(1).unwrap_or({ LL | 42 + 42 LL | + 42 + 42 + 42 LL | + 42 + 42 + 42 @@ -106,13 +115,13 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:114:5 + --> $DIR/manual_unwrap_or.rs:121:5 | -LL | / match Ok("Bob") as Result<&str, &str> { +LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, LL | | Err(_) => "Alice", LL | | }; - | |_____^ help: replace with: `(Ok("Bob") as Result<&str, &str>).unwrap_or("Alice")` + | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors From 6533d8becfd198299d0bd38550dd6c574cbd194f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 22 Oct 2020 23:39:59 +0200 Subject: [PATCH 0792/1110] manual-unwrap-or / pr remarks, round 2 --- clippy_lints/src/manual_unwrap_or.rs | 4 +++- tests/ui/manual_unwrap_or.fixed | 4 ++++ tests/ui/manual_unwrap_or.rs | 7 +++++++ tests/ui/manual_unwrap_or.stderr | 21 +++++++++++++++------ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index f3f1e31abde..cc9ee28d027 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -111,7 +111,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let wrap_in_parens = !matches!(scrutinee, Expr { kind: ExprKind::Call(..), .. }); + let wrap_in_parens = !matches!(scrutinee, Expr { + kind: ExprKind::Call(..) | ExprKind::Path(_), .. + }); let l_paren = if wrap_in_parens { "(" } else { "" }; let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index c784de0f604..582f5c6f7a8 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -70,6 +70,10 @@ fn result_unwrap_or() { // int case Ok::(1).unwrap_or(42); + // int case, scrutinee is a binding + let a = Ok::(1); + a.unwrap_or(42); + // int case, suggestion must surround with parenthesis (Ok(1) as Result).unwrap_or(42); diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index df5f237c3fb..0e2b7ecadb3 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -88,6 +88,13 @@ fn result_unwrap_or() { Err(_) => 42, }; + // int case, scrutinee is a binding + let a = Ok::(1); + match a { + Ok(i) => i, + Err(_) => 42, + }; + // int case, suggestion must surround with parenthesis match Ok(1) as Result { Ok(i) => i, diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 5bc01bf4e68..6603ab43437 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -67,7 +67,16 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:92:5 + --> $DIR/manual_unwrap_or.rs:93:5 + | +LL | / match a { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `a.unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:99:5 | LL | / match Ok(1) as Result { LL | | Ok(i) => i, @@ -76,7 +85,7 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:98:5 + --> $DIR/manual_unwrap_or.rs:105:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -85,7 +94,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:104:5 + --> $DIR/manual_unwrap_or.rs:111:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -94,7 +103,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:111:5 + --> $DIR/manual_unwrap_or.rs:118:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -115,7 +124,7 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:121:5 + --> $DIR/manual_unwrap_or.rs:128:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -123,5 +132,5 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From e8f12d2f02644834282dec0c27710886f1e85ae6 Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Thu, 22 Oct 2020 23:53:50 +0200 Subject: [PATCH 0793/1110] Address review comments --- clippy_lints/src/types.rs | 53 +++++++++++++----------- tests/ui/unnecessary_cast_fixable.fixed | 6 ++- tests/ui/unnecessary_cast_fixable.rs | 2 + tests/ui/unnecessary_cast_fixable.stderr | 22 +++++++--- 4 files changed, 52 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 716d027e434..f4bb648d15a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; -use std::fmt::Display; use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; @@ -12,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1225,7 +1224,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts to the same type. + /// **What it does:** Checks for casts to the same type, casts of int literals to integer types + /// and casts of float literals to float types. /// /// **Why is this bad?** It's just unnecessary. /// @@ -1234,6 +1234,7 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let _ = 2i32 as i32; + /// let _ = 0.5 as f32; /// ``` pub UNNECESSARY_CAST, complexity, @@ -1599,7 +1600,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if let ExprKind::Cast(ref ex, _) = expr.kind { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); - if let ExprKind::Lit(ref lit) = ex.kind { + if let Some(lit) = get_numeric_literal(ex) { + let literal_str = snippet_opt(cx, lit.span).unwrap_or_default(); + if_chain! { if let LitKind::Int(n, _) = lit.node; if let Some(src) = snippet_opt(cx, lit.span); @@ -1609,25 +1612,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - show_unnecessary_cast(cx, expr, n , cast_from, cast_to); + show_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); return; } } match lit.node { - LitKind::Int(num, LitIntType::Unsuffixed) if cast_to.is_integral() => { - show_unnecessary_cast(cx, expr, num, cast_from, cast_to); - return; + LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - LitKind::Float(num, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - show_unnecessary_cast(cx, expr, num, cast_from, cast_to); - return; + LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - _ => (), - }; - - match lit.node { - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => (), _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( @@ -1652,13 +1649,21 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } -fn show_unnecessary_cast( - cx: &LateContext<'_>, - expr: &Expr<'_>, - num: Num, - cast_from: Ty<'_>, - cast_to: Ty<'_>, -) { +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { + match expr.kind { + ExprKind::Lit(ref lit) => Some(lit), + ExprKind::Unary(UnOp::UnNeg, e) => { + if let ExprKind::Lit(ref lit) = e.kind { + Some(lit) + } else { + None + } + }, + _ => None, + } +} + +fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; span_lint_and_sugg( cx, @@ -1666,7 +1671,7 @@ fn show_unnecessary_cast( expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", num, cast_to), + format!("{}_{}", literal_str, cast_to), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index ba52fc2703f..54853f4b8a2 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -20,8 +20,10 @@ fn main() { 0b11 as f64; 1_u32; - 16_i32; - 2_usize; + 0x10_i32; + 0b10_usize; + 0o73_u16; + 1_000_000_000_u32; 1.0_f64; 0.5_f32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 0d2115548fd..8da3d947702 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -22,6 +22,8 @@ fn main() { 1 as u32; 0x10 as i32; 0b10 as usize; + 0o73 as u16; + 1_000_000_000 as u32; 1.0 as f64; 0.5 as f32; diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 474e62c30d5..28fb9540afc 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -28,25 +28,37 @@ error: casting integer literal to `i32` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:23:5 | LL | 0x10 as i32; - | ^^^^^^^^^^^ help: try: `16_i32` + | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:24:5 | LL | 0b10 as usize; - | ^^^^^^^^^^^^^ help: try: `2_usize` + | ^^^^^^^^^^^^^ help: try: `0b10_usize` + +error: casting integer literal to `u16` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:25:5 + | +LL | 0o73 as u16; + | ^^^^^^^^^^^ help: try: `0o73_u16` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:26:5 + | +LL | 1_000_000_000 as u32; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors From 02f01104bfbb935ee1c3c3971ccf055173e4f82b Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 00:04:27 +0200 Subject: [PATCH 0794/1110] Add test case for negative literals --- clippy_lints/src/types.rs | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 3 +++ tests/ui/unnecessary_cast_fixable.rs | 3 +++ tests/ui/unnecessary_cast_fixable.stderr | 14 +++++++++++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f4bb648d15a..3a088709a7e 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1601,7 +1601,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); if let Some(lit) = get_numeric_literal(ex) { - let literal_str = snippet_opt(cx, lit.span).unwrap_or_default(); + let literal_str = snippet_opt(cx, ex.span).unwrap_or_default(); if_chain! { if let LitKind::Int(n, _) = lit.node; diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 54853f4b8a2..2a13469b146 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -29,4 +29,7 @@ fn main() { 0.5_f32; 1.0 as u16; + + -1_i32; + -1.0_f32; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 8da3d947702..65ddd3c7fbf 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -29,4 +29,7 @@ fn main() { 0.5 as f32; 1.0 as u16; + + -1 as i32; + -1.0 as f32; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 28fb9540afc..26b23e315e3 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -60,5 +60,17 @@ error: casting float literal to `f32` is unnecessary LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` -error: aborting due to 10 previous errors +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:33:5 + | +LL | -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:34:5 + | +LL | -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` + +error: aborting due to 12 previous errors From 30f80c3b8c4fcb5d0db37b84a77a58303322cf4e Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 00:04:27 +0200 Subject: [PATCH 0795/1110] Fix test file --- tests/ui/unnecessary_cast_fixable.fixed | 4 ++-- tests/ui/unnecessary_cast_fixable.rs | 4 ++-- tests/ui/unnecessary_cast_fixable.stderr | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 2a13469b146..5aeb0340b26 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -30,6 +30,6 @@ fn main() { 1.0 as u16; - -1_i32; - -1.0_f32; + let _ = -1_i32; + let _ = -1.0_f32; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 65ddd3c7fbf..0f249c23055 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -30,6 +30,6 @@ fn main() { 1.0 as u16; - -1 as i32; - -1.0 as f32; + let _ = -1 as i32; + let _ = -1.0 as f32; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 26b23e315e3..5100e9798c4 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -61,16 +61,16 @@ LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 + --> $DIR/unnecessary_cast_fixable.rs:33:13 | -LL | -1 as i32; - | ^^^^^^^^^ help: try: `-1_i32` +LL | let _ = -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 + --> $DIR/unnecessary_cast_fixable.rs:34:13 | -LL | -1.0 as f32; - | ^^^^^^^^^^^ help: try: `-1.0_f32` +LL | let _ = -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` error: aborting due to 12 previous errors From 3807634a470b572303d95feb8a5db273c7cea4af Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sun, 11 Oct 2020 16:04:33 -0700 Subject: [PATCH 0796/1110] clippy_lints: Update empty_loop lint We also update the documentation to note that the remediations are different for `std` and `no_std` crates. Signed-off-by: Joe Richey --- clippy_lints/src/loops.rs | 29 +++++++++++++++---- tests/ui/crashes/ice-360.stderr | 3 +- tests/ui/empty_loop.stderr | 11 +++++-- .../{issue-3746.rs => empty_loop_no_std.rs} | 0 4 files changed, 33 insertions(+), 10 deletions(-) rename tests/ui/{issue-3746.rs => empty_loop_no_std.rs} (100%) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 63d7e3176b1..bae12943869 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -293,9 +293,24 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for empty `loop` expressions. /// - /// **Why is this bad?** Those busy loops burn CPU cycles without doing - /// anything. Think of the environment and either block on something or at least - /// make the thread sleep for some microseconds. + /// **Why is this bad?** These busy loops burn CPU cycles without doing + /// anything. It is _almost always_ a better idea to `panic!` than to have + /// a busy loop. + /// + /// If panicking isn't possible, think of the environment and either: + /// - block on something + /// - sleep the thread for some microseconds + /// - yield or pause the thread + /// + /// For `std` targets, this can be done with + /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) + /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). + /// + /// For `no_std` targets, doing this is more complicated, especially because + /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will + /// probably need to invoke some target-specific intrinsic. Examples include: + /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) + /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) /// /// **Known problems:** None. /// @@ -502,13 +517,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // (even if the "match" or "if let" is used for declaration) if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { // also check for empty `loop {}` statements + // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler]) if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { - span_lint( + span_lint_and_help( cx, EMPTY_LOOP, expr.span, - "empty `loop {}` detected. You may want to either use `panic!()` or add \ - `std::thread::sleep(..);` to the loop body.", + "empty `loop {}` wastes CPU cycles", + None, + "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.", ); } diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index 84e31eaf2e9..bb03ce40355 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -12,13 +12,14 @@ LL | | } | = note: `-D clippy::while-let-loop` implied by `-D warnings` -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/ice-360.rs:10:9 | LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: aborting due to 2 previous errors diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index e44c58ea770..fd3979f259a 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -1,22 +1,27 @@ -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:9:5 | LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:11:9 | LL | loop {} | ^^^^^^^ + | + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:15:9 | LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ + | + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: aborting due to 3 previous errors diff --git a/tests/ui/issue-3746.rs b/tests/ui/empty_loop_no_std.rs similarity index 100% rename from tests/ui/issue-3746.rs rename to tests/ui/empty_loop_no_std.rs From d46edd99667ad342e6118b2216a0c24ee009e86c Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 23:40:57 +0200 Subject: [PATCH 0797/1110] Keep sign in int-to-float casts --- clippy_lints/src/types.rs | 16 ++++++++-- tests/ui/unnecessary_cast_fixable.fixed | 3 ++ tests/ui/unnecessary_cast_fixable.rs | 3 ++ tests/ui/unnecessary_cast_fixable.stderr | 38 +++++++++++++++++------- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3a088709a7e..6a33aaaaab2 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1236,6 +1236,13 @@ declare_clippy_lint! { /// let _ = 2i32 as i32; /// let _ = 0.5 as f32; /// ``` + /// + /// Better: + /// + /// ```rust + /// let _ = 2_i32; + /// let _ = 0.5_f32; + /// ``` pub UNNECESSARY_CAST, complexity, "cast to the same type, e.g., `x as i32` where `x: i32`" @@ -1612,7 +1619,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - show_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() }; + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); return; } } @@ -1624,7 +1632,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => (), + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( @@ -1649,6 +1657,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } +fn is_unary_neg(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Unary(UnOp::UnNeg, _)) +} + fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { match expr.kind { ExprKind::Lit(ref lit) => Some(lit), diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 5aeb0340b26..350da4965d1 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -8,6 +8,9 @@ fn main() { 100_f32; 100_f64; 100_f64; + let _ = -100_f32; + let _ = -100_f64; + let _ = -100_f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 0f249c23055..ad2fb2e6289 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -8,6 +8,9 @@ fn main() { 100 as f32; 100 as f64; 100_i32 as f64; + let _ = -100 as f32; + let _ = -100 as f64; + let _ = -100_i32 as f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 5100e9798c4..5a210fc8909 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -18,59 +18,77 @@ error: casting integer literal to `f64` is unnecessary LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:11:13 + | +LL | let _ = -100 as f32; + | ^^^^^^^^^^^ help: try: `-100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:12:13 + | +LL | let _ = -100 as f64; + | ^^^^^^^^^^^ help: try: `-100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:13:13 + | +LL | let _ = -100_i32 as f64; + | ^^^^^^^^^^^^^^^ help: try: `-100_f64` + error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:22:5 + --> $DIR/unnecessary_cast_fixable.rs:25:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:23:5 + --> $DIR/unnecessary_cast_fixable.rs:26:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:24:5 + --> $DIR/unnecessary_cast_fixable.rs:27:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:25:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:31:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:32:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:13 + --> $DIR/unnecessary_cast_fixable.rs:36:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:13 + --> $DIR/unnecessary_cast_fixable.rs:37:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors From 62f60e1ae5bd2287497746bf90a302903e0ae462 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 24 Oct 2020 09:31:32 +0200 Subject: [PATCH 0798/1110] No lint with `cfg!` and fix sugg for macro in `needless_bool` lint --- clippy_lints/src/needless_bool.rs | 16 +++++++- tests/ui/bool_comparison.fixed | 40 +++++++++++++++++++- tests/ui/bool_comparison.rs | 40 +++++++++++++++++++- tests/ui/bool_comparison.stderr | 62 +++++++++++++++++++++---------- 4 files changed, 135 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index dc5aa669139..a799a644e97 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,9 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{ + higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -233,6 +235,9 @@ fn check_comparison<'a, 'tcx>( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), ); + if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() { + return; + } if l_ty.is_bool() && r_ty.is_bool() { let mut applicability = Applicability::MachineApplicable; @@ -295,7 +300,14 @@ fn suggest_bool_comparison<'a, 'tcx>( message: &str, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, ) { - let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability); + let hint = if expr.span.from_expansion() { + if applicability != Applicability::Unspecified { + applicability = Applicability::MaybeIncorrect; + } + Sugg::hir_with_macro_callsite(cx, expr, "..") + } else { + Sugg::hir_with_applicability(cx, expr, "..", &mut applicability) + }; span_lint_and_sugg( cx, BOOL_COMPARISON, diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index 91211764759..5a012ff4d27 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,6 +1,7 @@ // run-rustfix -#[warn(clippy::bool_comparison)] +#![warn(clippy::bool_comparison)] + fn main() { let x = true; if x { @@ -127,3 +128,40 @@ fn issue4983() { if b == a {}; if !b == !a {}; } + +macro_rules! m { + ($func:ident) => { + $func() + }; +} + +fn func() -> bool { + true +} + +#[allow(dead_code)] +fn issue3973() { + // ok, don't lint on `cfg` invocation + if false == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == false {} + if true == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == true {} + + // lint, could be simplified + if !m!(func) {} + if !m!(func) {} + if m!(func) {} + if m!(func) {} + + // no lint with a variable + let is_debug = false; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} + let is_debug = true; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} +} diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 01ee35859f0..c534bc25c20 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,6 +1,7 @@ // run-rustfix -#[warn(clippy::bool_comparison)] +#![warn(clippy::bool_comparison)] + fn main() { let x = true; if x == true { @@ -127,3 +128,40 @@ fn issue4983() { if b == a {}; if !b == !a {}; } + +macro_rules! m { + ($func:ident) => { + $func() + }; +} + +fn func() -> bool { + true +} + +#[allow(dead_code)] +fn issue3973() { + // ok, don't lint on `cfg` invocation + if false == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == false {} + if true == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == true {} + + // lint, could be simplified + if false == m!(func) {} + if m!(func) == false {} + if true == m!(func) {} + if m!(func) == true {} + + // no lint with a variable + let is_debug = false; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} + let is_debug = true; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} +} diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index 55d94b8257d..31522d4a525 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -1,5 +1,5 @@ error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:6:8 + --> $DIR/bool_comparison.rs:7:8 | LL | if x == true { | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -7,106 +7,130 @@ LL | if x == true { = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:11:8 + --> $DIR/bool_comparison.rs:12:8 | LL | if x == false { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:16:8 + --> $DIR/bool_comparison.rs:17:8 | LL | if true == x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:21:8 + --> $DIR/bool_comparison.rs:22:8 | LL | if false == x { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:26:8 + --> $DIR/bool_comparison.rs:27:8 | LL | if x != true { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:31:8 + --> $DIR/bool_comparison.rs:32:8 | LL | if x != false { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:36:8 + --> $DIR/bool_comparison.rs:37:8 | LL | if true != x { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:41:8 + --> $DIR/bool_comparison.rs:42:8 | LL | if false != x { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:46:8 + --> $DIR/bool_comparison.rs:47:8 | LL | if x < true { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:51:8 + --> $DIR/bool_comparison.rs:52:8 | LL | if false < x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:56:8 + --> $DIR/bool_comparison.rs:57:8 | LL | if x > false { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:61:8 + --> $DIR/bool_comparison.rs:62:8 | LL | if true > x { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:67:8 + --> $DIR/bool_comparison.rs:68:8 | LL | if x < y { | ^^^^^ help: try simplifying it as shown: `!x & y` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:72:8 + --> $DIR/bool_comparison.rs:73:8 | LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:120:8 + --> $DIR/bool_comparison.rs:121:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:121:8 + --> $DIR/bool_comparison.rs:122:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:125:8 + --> $DIR/bool_comparison.rs:126:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:126:8 + --> $DIR/bool_comparison.rs:127:8 | LL | if !b == a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: aborting due to 18 previous errors +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:151:8 + | +LL | if false == m!(func) {} + | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:152:8 + | +LL | if m!(func) == false {} + | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:153:8 + | +LL | if true == m!(func) {} + | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:154:8 + | +LL | if m!(func) == true {} + | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + +error: aborting due to 22 previous errors From 0d21ae0e194fd8f7f1f67bf1921910e0ca21a32c Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 24 Oct 2020 11:35:05 +0200 Subject: [PATCH 0799/1110] manual-unwrap-or / pr remarks, round 3 --- clippy_lints/src/manual_unwrap_or.rs | 13 +++---------- tests/ui/manual_unwrap_or.fixed | 12 +++++++++++- tests/ui/manual_unwrap_or.rs | 15 ++++++++++++++- tests/ui/manual_unwrap_or.stderr | 19 ++++++++++++++----- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index cc9ee28d027..22aa37e41fe 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,5 +1,6 @@ use crate::consts::constant_simple; use crate::utils; +use crate::utils::sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath}; @@ -104,28 +105,20 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { None }; if let Some(or_arm) = applicable_or_arm(match_arms); - if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { let reindented_or_body = utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let wrap_in_parens = !matches!(scrutinee, Expr { - kind: ExprKind::Call(..) | ExprKind::Path(_), .. - }); - let l_paren = if wrap_in_parens { "(" } else { "" }; - let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), "replace with", format!( - "{}{}{}.unwrap_or({})", - l_paren, - scrutinee_snippet, - r_paren, + "{}.unwrap_or({})", + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(), reindented_or_body, ), Applicability::MachineApplicable, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 582f5c6f7a8..5aa5a43cb92 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -74,9 +74,19 @@ fn result_unwrap_or() { let a = Ok::(1); a.unwrap_or(42); - // int case, suggestion must surround with parenthesis + // int case, suggestion must surround Result expr with parenthesis (Ok(1) as Result).unwrap_or(42); + // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + struct S {} + impl S { + fn method(self) -> Option { + Some(42) + } + } + let s = S {}; + s.method().unwrap_or(42); + // int case reversed Ok::(1).unwrap_or(42); diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 0e2b7ecadb3..df534031f54 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -95,12 +95,25 @@ fn result_unwrap_or() { Err(_) => 42, }; - // int case, suggestion must surround with parenthesis + // int case, suggestion must surround Result expr with parenthesis match Ok(1) as Result { Ok(i) => i, Err(_) => 42, }; + // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + struct S {} + impl S { + fn method(self) -> Option { + Some(42) + } + } + let s = S {}; + match s.method() { + Some(i) => i, + None => 42, + }; + // int case reversed match Ok::(1) { Err(_) => 42, diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 6603ab43437..fc174c4c270 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -84,8 +84,17 @@ LL | | Err(_) => 42, LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:112:5 + | +LL | / match s.method() { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `s.method().unwrap_or(42)` + error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:105:5 + --> $DIR/manual_unwrap_or.rs:118:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -94,7 +103,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:111:5 + --> $DIR/manual_unwrap_or.rs:124:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -103,7 +112,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:118:5 + --> $DIR/manual_unwrap_or.rs:131:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -124,7 +133,7 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:128:5 + --> $DIR/manual_unwrap_or.rs:141:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -132,5 +141,5 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors From 71ac0c0be81019eb81b55eb75ae6318b3d0482ea Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Sat, 24 Oct 2020 14:07:34 +0200 Subject: [PATCH 0800/1110] Keep separators in cast_size_32bits stderr --- tests/ui/cast_size_32bit.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size_32bit.stderr index 2eec51895f5..140676a5ffc 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size_32bit.stderr @@ -124,7 +124,7 @@ error: casting integer literal to `f64` is unnecessary --> $DIR/cast_size_32bit.rs:34:5 | LL | 3_999_999_999usize as f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` From e8731a926c9a642ca1ddf5b52baf40e0a8873d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Baasch=20de=20Souza?= Date: Thu, 8 Oct 2020 02:17:32 -0300 Subject: [PATCH 0801/1110] Add large_types_passed_by_value lint Refactor trivially_copy_pass_by_ref and the new lint into pass_by_ref_or_value module Update stderr of conf_unknown_key test Rename lint to large_types_passed_by_value Increase `pass_by_value_size_limit` default value to 256 Improve rules for `large_types_passed_by_value` Improve tests for `large_types_passed_by_value` Improve documentation for `large_types_passed_by_value` Make minor corrections to pass_by_ref_or_value.rs suggested by clippy itself Fix `large_types_passed_by_value` example and improve docs pass_by_ref_or_value: Tweak check for mut annotation in params large_types_passed_by_value: add tests for pub trait, trait impl and inline attributes --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 13 +- clippy_lints/src/pass_by_ref_or_value.rs | 256 ++++++++++++++++++ .../src/trivially_copy_pass_by_ref.rs | 183 ------------- clippy_lints/src/utils/conf.rs | 2 + src/lintlist/mod.rs | 9 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/large_types_passed_by_value.rs | 66 +++++ tests/ui/large_types_passed_by_value.stderr | 52 ++++ 9 files changed, 394 insertions(+), 190 deletions(-) create mode 100644 clippy_lints/src/pass_by_ref_or_value.rs delete mode 100644 clippy_lints/src/trivially_copy_pass_by_ref.rs create mode 100644 tests/ui/large_types_passed_by_value.rs create mode 100644 tests/ui/large_types_passed_by_value.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..22f96398153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1779,6 +1779,7 @@ Released 2018-09-13 [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..1a950a7c334 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -278,6 +278,7 @@ mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; mod partialeq_ne_impl; +mod pass_by_ref_or_value; mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; @@ -311,7 +312,6 @@ mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; -mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; @@ -776,6 +776,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &panic_unimplemented::UNIMPLEMENTED, &panic_unimplemented::UNREACHABLE, &partialeq_ne_impl::PARTIALEQ_NE_IMPL, + &pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, + &pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, &pattern_type_mismatch::PATTERN_TYPE_MISMATCH, &precedence::PRECEDENCE, @@ -835,7 +837,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &transmute::USELESS_TRANSMUTE, &transmute::WRONG_TRANSMUTE, &transmuting_null::TRANSMUTING_NULL, - &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF, &try_err::TRY_ERR, &types::ABSURD_EXTREME_COMPARISONS, &types::BORROWED_BOX, @@ -1009,11 +1010,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)); store.register_late_pass(|| box explicit_write::ExplicitWrite); store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue); - let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new( + let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( conf.trivial_copy_size_limit, + conf.pass_by_value_size_limit, &sess.target, ); - store.register_late_pass(move || box trivially_copy_pass_by_ref); + store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); @@ -1237,13 +1239,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), + LintId::of(&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), + LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), LintId::of(&types::CAST_POSSIBLE_TRUNCATION), LintId::of(&types::CAST_POSSIBLE_WRAP), diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs new file mode 100644 index 00000000000..28816c3076d --- /dev/null +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -0,0 +1,256 @@ +use std::cmp; + +use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::attr; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; +use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; +use rustc_target::spec::Target; + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by reference, where + /// the argument type is `Copy` and small enough to be more efficient to always + /// pass by value. + /// + /// **Why is this bad?** In many calling conventions instances of structs will + /// be passed through registers if they fit into two or less general purpose + /// registers. + /// + /// **Known problems:** This lint is target register size dependent, it is + /// limited to 32-bit to try and reduce portability problems between 32 and + /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit + /// will be different. + /// + /// The configuration option `trivial_copy_size_limit` can be set to override + /// this limit for a project. + /// + /// This lint attempts to allow passing arguments by reference if a reference + /// to that argument is returned. This is implemented by comparing the lifetime + /// of the argument and return value for equality. However, this can cause + /// false positives in cases involving multiple lifetimes that are bounded by + /// each other. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// fn foo(v: &u32) {} + /// ``` + /// + /// ```rust + /// // Better + /// fn foo(v: u32) {} + /// ``` + pub TRIVIALLY_COPY_PASS_BY_REF, + pedantic, + "functions taking small copyable arguments by reference" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by value, where + /// the argument type is `Copy` and large enough to be worth considering + /// passing by reference. Does not trigger if the function is being exported, + /// because that might induce API breakage, if the parameter is declared as mutable, + /// or if the argument is a `self`. + /// + /// **Why is this bad?** Arguments passed by value might result in an unnecessary + /// shallow copy, taking up more space in the stack and requiring a call to + /// `memcpy`, which which can be expensive. + /// + /// **Example:** + /// + /// ```rust + /// #[derive(Clone, Copy)] + /// struct TooLarge([u8; 2048]); + /// + /// // Bad + /// fn foo(v: TooLarge) {} + /// ``` + /// ```rust + /// #[derive(Clone, Copy)] + /// struct TooLarge([u8; 2048]); + /// + /// // Good + /// fn foo(v: &TooLarge) {} + /// ``` + pub LARGE_TYPES_PASSED_BY_VALUE, + pedantic, + "functions taking large arguments by value" +} + +#[derive(Copy, Clone)] +pub struct PassByRefOrValue { + ref_min_size: u64, + value_max_size: u64, +} + +impl<'tcx> PassByRefOrValue { + pub fn new(ref_min_size: Option, value_max_size: u64, target: &Target) -> Self { + let ref_min_size = ref_min_size.unwrap_or_else(|| { + let bit_width = u64::from(target.pointer_width); + // Cap the calculated bit width at 32-bits to reduce + // portability problems between 32 and 64-bit targets + let bit_width = cmp::min(bit_width, 32); + #[allow(clippy::integer_division)] + let byte_width = bit_width / 8; + // Use a limit of 2 times the register byte width + byte_width * 2 + }); + + Self { + ref_min_size, + value_max_size, + } + } + + fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { + let fn_def_id = cx.tcx.hir().local_def_id(hir_id); + + let fn_sig = cx.tcx.fn_sig(fn_def_id); + let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); + + let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id)); + + for (index, (input, &ty)) in decl.inputs.iter().zip(fn_sig.inputs()).enumerate() { + // All spans generated from a proc-macro invocation are the same... + match span { + Some(s) if s == input.span => return, + _ => (), + } + + match ty.kind() { + ty::Ref(input_lt, ty, Mutability::Not) => { + // Use lifetimes to determine if we're returning a reference to the + // argument. In that case we can't switch to pass-by-value as the + // argument will not live long enough. + let output_lts = match *fn_sig.output().kind() { + ty::Ref(output_lt, _, _) => vec![output_lt], + ty::Adt(_, substs) => substs.regions().collect(), + _ => vec![], + }; + + if_chain! { + if !output_lts.contains(&input_lt); + if is_copy(cx, ty); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size <= self.ref_min_size; + if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; + then { + let value_type = if is_self_ty(decl_ty) { + "self".into() + } else { + snippet(cx, decl_ty.span, "_").into() + }; + span_lint_and_sugg( + cx, + TRIVIALLY_COPY_PASS_BY_REF, + input.span, + &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size), + "consider passing by value instead", + value_type, + Applicability::Unspecified, + ); + } + } + }, + + ty::Adt(_, _) | ty::Array(_, _) | ty::Tuple(_) => { + // if function has a body and parameter is annotated with mut, ignore + if let Some(param) = fn_body.and_then(|body| body.params.get(index)) { + match param.pat.kind { + PatKind::Binding(BindingAnnotation::Unannotated, _, _, _) => {}, + _ => continue, + } + } + + if_chain! { + if !cx.access_levels.is_exported(hir_id); + if is_copy(cx, ty); + if !is_self_ty(input); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size > self.value_max_size; + then { + span_lint_and_sugg( + cx, + LARGE_TYPES_PASSED_BY_VALUE, + input.span, + &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size), + "consider passing by reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); + } + } + }, + + _ => {}, + } + } + } +} + +impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]); + +impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if item.span.from_expansion() { + return; + } + + if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { + self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + _body: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + if span.from_expansion() { + return; + } + + match kind { + FnKind::ItemFn(.., header, _, attrs) => { + if header.abi != Abi::Rust { + return; + } + for a in attrs { + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } + } + } + }, + FnKind::Method(..) => (), + FnKind::Closure(..) => return, + } + + // Exclude non-inherent impls + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | + ItemKind::Trait(..)) + { + return; + } + } + + self.check_poly_fn(cx, hir_id, decl, Some(span)); + } +} diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs deleted file mode 100644 index e90ea0fc200..00000000000 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::cmp; - -use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::attr; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; -use rustc_target::abi::LayoutOf; -use rustc_target::spec::abi::Abi; -use rustc_target::spec::Target; - -declare_clippy_lint! { - /// **What it does:** Checks for functions taking arguments by reference, where - /// the argument type is `Copy` and small enough to be more efficient to always - /// pass by value. - /// - /// **Why is this bad?** In many calling conventions instances of structs will - /// be passed through registers if they fit into two or less general purpose - /// registers. - /// - /// **Known problems:** This lint is target register size dependent, it is - /// limited to 32-bit to try and reduce portability problems between 32 and - /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit - /// will be different. - /// - /// The configuration option `trivial_copy_size_limit` can be set to override - /// this limit for a project. - /// - /// This lint attempts to allow passing arguments by reference if a reference - /// to that argument is returned. This is implemented by comparing the lifetime - /// of the argument and return value for equality. However, this can cause - /// false positives in cases involving multiple lifetimes that are bounded by - /// each other. - /// - /// **Example:** - /// - /// ```rust - /// // Bad - /// fn foo(v: &u32) {} - /// ``` - /// - /// ```rust - /// // Better - /// fn foo(v: u32) {} - /// ``` - pub TRIVIALLY_COPY_PASS_BY_REF, - pedantic, - "functions taking small copyable arguments by reference" -} - -#[derive(Copy, Clone)] -pub struct TriviallyCopyPassByRef { - limit: u64, -} - -impl<'tcx> TriviallyCopyPassByRef { - pub fn new(limit: Option, target: &Target) -> Self { - let limit = limit.unwrap_or_else(|| { - let bit_width = u64::from(target.pointer_width); - // Cap the calculated bit width at 32-bits to reduce - // portability problems between 32 and 64-bit targets - let bit_width = cmp::min(bit_width, 32); - #[allow(clippy::integer_division)] - let byte_width = bit_width / 8; - // Use a limit of 2 times the register byte width - byte_width * 2 - }); - Self { limit } - } - - fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { - let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - - let fn_sig = cx.tcx.fn_sig(fn_def_id); - let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); - - // Use lifetimes to determine if we're returning a reference to the - // argument. In that case we can't switch to pass-by-value as the - // argument will not live long enough. - let output_lts = match *fn_sig.output().kind() { - ty::Ref(output_lt, _, _) => vec![output_lt], - ty::Adt(_, substs) => substs.regions().collect(), - _ => vec![], - }; - - for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) { - // All spans generated from a proc-macro invocation are the same... - match span { - Some(s) if s == input.span => return, - _ => (), - } - - if_chain! { - if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind(); - if !output_lts.contains(&input_lt); - if is_copy(cx, ty); - if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); - if size <= self.limit; - if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; - then { - let value_type = if is_self_ty(decl_ty) { - "self".into() - } else { - snippet(cx, decl_ty.span, "_").into() - }; - span_lint_and_sugg( - cx, - TRIVIALLY_COPY_PASS_BY_REF, - input.span, - &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit), - "consider passing by value instead", - value_type, - Applicability::Unspecified, - ); - } - } - } - } -} - -impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]); - -impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if item.span.from_expansion() { - return; - } - - if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - _body: &'tcx Body<'_>, - span: Span, - hir_id: HirId, - ) { - if span.from_expansion() { - return; - } - - match kind { - FnKind::ItemFn(.., header, _, attrs) => { - if header.abi != Abi::Rust { - return; - } - for a in attrs { - if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym!(proc_macro_derive)) - || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) - { - return; - } - } - } - }, - FnKind::Method(..) => (), - FnKind::Closure(..) => return, - } - - // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { - return; - } - } - - self.check_poly_fn(cx, hir_id, decl, Some(span)); - } -} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index dd2fd0bb445..0ac8fff69f0 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -150,6 +150,8 @@ define_Conf! { (literal_representation_threshold, "literal_representation_threshold": u64, 16384), /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit, "trivial_copy_size_limit": Option, None), + /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value. + (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256), /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have (too_many_lines_threshold, "too_many_lines_threshold": u64, 100), /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..f3536f26339 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1061,6 +1061,13 @@ vec![ deprecation: None, module: "large_stack_arrays", }, + Lint { + name: "large_types_passed_by_value", + group: "pedantic", + desc: "functions taking large arguments by value", + deprecation: None, + module: "pass_by_ref_or_value", + }, Lint { name: "len_without_is_empty", group: "style", @@ -2389,7 +2396,7 @@ vec![ group: "pedantic", desc: "functions taking small copyable arguments by reference", deprecation: None, - module: "trivially_copy_pass_by_ref", + module: "pass_by_ref_or_value", }, Lint { name: "try_err", diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 103ec27e7d7..a58e7e918e2 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/large_types_passed_by_value.rs b/tests/ui/large_types_passed_by_value.rs new file mode 100644 index 00000000000..e4a2e9df4d7 --- /dev/null +++ b/tests/ui/large_types_passed_by_value.rs @@ -0,0 +1,66 @@ +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" + +#![warn(clippy::large_types_passed_by_value)] + +pub struct Large([u8; 2048]); + +#[derive(Clone, Copy)] +pub struct LargeAndCopy([u8; 2048]); + +pub struct Small([u8; 4]); + +#[derive(Clone, Copy)] +pub struct SmallAndCopy([u8; 4]); + +fn small(a: Small, b: SmallAndCopy) {} +fn not_copy(a: Large) {} +fn by_ref(a: &Large, b: &LargeAndCopy) {} +fn mutable(mut a: LargeAndCopy) {} +fn bad(a: LargeAndCopy) {} +pub fn bad_but_pub(a: LargeAndCopy) {} + +impl LargeAndCopy { + fn self_is_ok(self) {} + fn other_is_not_ok(self, other: LargeAndCopy) {} + fn unless_other_can_change(self, mut other: LargeAndCopy) {} + pub fn or_were_in_public(self, other: LargeAndCopy) {} +} + +trait LargeTypeDevourer { + fn devoure_array(&self, array: [u8; 6666]); + fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)); + fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); +} + +pub trait PubLargeTypeDevourer { + fn devoure_array_in_public(&self, array: [u8; 6666]); +} + +struct S {} +impl LargeTypeDevourer for S { + fn devoure_array(&self, array: [u8; 6666]) { + todo!(); + } + fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)) { + todo!(); + } + fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)) { + todo!(); + } +} + +#[inline(always)] +fn foo_always(x: LargeAndCopy) { + todo!(); +} +#[inline(never)] +fn foo_never(x: LargeAndCopy) { + todo!(); +} +#[inline] +fn foo(x: LargeAndCopy) { + todo!(); +} + +fn main() {} diff --git a/tests/ui/large_types_passed_by_value.stderr b/tests/ui/large_types_passed_by_value.stderr new file mode 100644 index 00000000000..5f42dcfb9b5 --- /dev/null +++ b/tests/ui/large_types_passed_by_value.stderr @@ -0,0 +1,52 @@ +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:20:11 + | +LL | fn bad(a: LargeAndCopy) {} + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + | + = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:25:37 + | +LL | fn other_is_not_ok(self, other: LargeAndCopy) {} + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:31:36 + | +LL | fn devoure_array(&self, array: [u8; 6666]); + | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:32:34 + | +LL | fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:33:50 + | +LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:33:67 + | +LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:58:17 + | +LL | fn foo_never(x: LargeAndCopy) { + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:62:11 + | +LL | fn foo(x: LargeAndCopy) { + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: aborting due to 8 previous errors + From f82f9c2c55392ef9d7649bf2e485f7e509fd0038 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 2 Oct 2020 09:05:30 +0800 Subject: [PATCH 0802/1110] Add lint for `&mut Mutex::lock` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 7 +++ clippy_lints/src/mut_mutex_lock.rs | 75 ++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/mut_mutex_lock.rs | 19 ++++++++ tests/ui/mut_mutex_lock.stderr | 19 ++++++++ 6 files changed, 128 insertions(+) create mode 100644 clippy_lints/src/mut_mutex_lock.rs create mode 100644 tests/ui/mut_mutex_lock.rs create mode 100644 tests/ui/mut_mutex_lock.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d995cc9701..ba080835f5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1843,6 +1843,7 @@ Released 2018-09-13 [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref [`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock [`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound [`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8d53b9799f0..3a108bcfe6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -255,6 +255,7 @@ mod modulo_arithmetic; mod multiple_crate_versions; mod mut_key; mod mut_mut; +mod mut_mutex_lock; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; @@ -744,6 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, &mut_key::MUTABLE_KEY_TYPE, &mut_mut::MUT_MUT, + &mut_mutex_lock::MUT_MUTEX_LOCK, &mut_reference::UNNECESSARY_MUT_PASSED, &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, &mutex_atomic::MUTEX_ATOMIC, @@ -1112,6 +1114,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); + store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); @@ -1446,6 +1449,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mutex_atomic::MUTEX_ATOMIC), LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), @@ -1780,6 +1784,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs new file mode 100644 index 00000000000..4f3108355ca --- /dev/null +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -0,0 +1,75 @@ +use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `&mut Mutex::lock` calls + /// + /// **Why is this bad?** `Mutex::lock` is less efficient than + /// calling `Mutex::get_mut` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.lock().unwrap(); + /// do_stuff(value); + /// ``` + /// Use instead: + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.get_mut().unwrap(); + /// do_stuff(value); + /// ``` + pub MUT_MUTEX_LOCK, + correctness, + "`&mut Mutex::lock` does unnecessary locking" +} + +declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); + +impl<'tcx> LateLintPass<'tcx> for MutMutexLock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { + if_chain! { + if is_mut_mutex_lock_call(cx, ex).is_some(); + then { + span_lint_and_help( + cx, + MUT_MUTEX_LOCK, + ex.span, + "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", + None, + "use `&mut Mutex::get_mut` instead", + ); + } + } + } +} + +fn is_mut_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; + if path.ident.name == sym!(lock); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); + if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); + then { + Some(&args[0]) + } else { + None + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 98ad994ea7b..5e48757a4c5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1502,6 +1502,13 @@ vec![ deprecation: None, module: "mut_mut", }, + Lint { + name: "mut_mutex_lock", + group: "correctness", + desc: "`&mut Mutex::lock` does unnecessary locking", + deprecation: None, + module: "mut_mutex_lock", + }, Lint { name: "mut_range_bound", group: "complexity", diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs new file mode 100644 index 00000000000..516d44bb7a9 --- /dev/null +++ b/tests/ui/mut_mutex_lock.rs @@ -0,0 +1,19 @@ +#![warn(clippy::mut_mutex_lock)] + +use std::sync::{Arc, Mutex}; + +fn mut_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + + let value = value_mutex.lock().unwrap(); + *value += 1; +} + +fn no_owned_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value = value_rc.lock().unwrap(); + *value += 1; +} + +fn main() {} diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr new file mode 100644 index 00000000000..426e0240830 --- /dev/null +++ b/tests/ui/mut_mutex_lock.stderr @@ -0,0 +1,19 @@ +error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable + --> $DIR/mut_mutex_lock.rs:10:6 + | +LL | let value = value_mutex.lock().unwrap(); + | ----- help: consider changing this to be mutable: `mut value` +LL | *value += 1; + | ^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable + --> $DIR/mut_mutex_lock.rs:16:6 + | +LL | let value = value_rc.lock().unwrap(); + | ----- help: consider changing this to be mutable: `mut value` +LL | *value += 1; + | ^^^^^ cannot borrow as mutable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`. From fb8a9cb38d73f3876b57f250502895c7cc60d421 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 2 Oct 2020 10:54:44 +0800 Subject: [PATCH 0803/1110] Change lint doc test --- clippy_lints/src/mut_mutex_lock.rs | 6 +++--- tests/ui/mut_mutex_lock.rs | 4 ++-- tests/ui/mut_mutex_lock.stderr | 22 +++++++--------------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 4f3108355ca..0680e578537 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -21,8 +21,8 @@ declare_clippy_lint! { /// let mut value_rc = Arc::new(Mutex::new(42_u8)); /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); /// - /// let value = value_mutex.lock().unwrap(); - /// do_stuff(value); + /// let mut value = value_mutex.lock().unwrap(); + /// *value += 1; /// ``` /// Use instead: /// ```rust @@ -32,7 +32,7 @@ declare_clippy_lint! { /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); /// /// let value = value_mutex.get_mut().unwrap(); - /// do_stuff(value); + /// *value += 1; /// ``` pub MUT_MUTEX_LOCK, correctness, diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 516d44bb7a9..9cd98e90c29 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -6,13 +6,13 @@ fn mut_mutex_lock() { let mut value_rc = Arc::new(Mutex::new(42_u8)); let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - let value = value_mutex.lock().unwrap(); + let mut value = value_mutex.lock().unwrap(); *value += 1; } fn no_owned_mutex_lock() { let mut value_rc = Arc::new(Mutex::new(42_u8)); - let value = value_rc.lock().unwrap(); + let mut value = value_rc.lock().unwrap(); *value += 1; } diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index 426e0240830..d521ebb56c4 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,19 +1,11 @@ -error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable - --> $DIR/mut_mutex_lock.rs:10:6 +error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference + --> $DIR/mut_mutex_lock.rs:9:21 | -LL | let value = value_mutex.lock().unwrap(); - | ----- help: consider changing this to be mutable: `mut value` -LL | *value += 1; - | ^^^^^ cannot borrow as mutable - -error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable - --> $DIR/mut_mutex_lock.rs:16:6 +LL | let mut value = value_mutex.lock().unwrap(); + | ^^^^^^^^^^^^^^^^^^ | -LL | let value = value_rc.lock().unwrap(); - | ----- help: consider changing this to be mutable: `mut value` -LL | *value += 1; - | ^^^^^ cannot borrow as mutable + = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` + = help: use `&mut Mutex::get_mut` instead -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0596`. From ec0c3afa7303383ee039c252b59b56023052ae2e Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sat, 3 Oct 2020 07:53:45 +0800 Subject: [PATCH 0804/1110] Run update_lints --- clippy_lints/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3a108bcfe6a..648c65ef7b0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1785,8 +1785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), From 77e34a69bbbec0ef05dee9750ff3e7db4eb35d59 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Mon, 5 Oct 2020 11:44:54 +0800 Subject: [PATCH 0805/1110] Inline is_mut_mutex_lock_call --- clippy_lints/src/mut_mutex_lock.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 0680e578537..ca3371a5d75 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -44,7 +44,11 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if is_mut_mutex_lock_call(cx, ex).is_some(); + if let ExprKind::MethodCall(path, _span, args, _) = &ex.kind; + if path.ident.name == sym!(lock); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); + if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { span_lint_and_help( cx, @@ -58,18 +62,3 @@ impl<'tcx> LateLintPass<'tcx> for MutMutexLock { } } } - -fn is_mut_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; - if path.ident.name == sym!(lock); - let ty = cx.typeck_results().expr_ty(&args[0]); - if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); - if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); - then { - Some(&args[0]) - } else { - None - } - } -} From 292cb9bfb6bc51595404425b0ada90f21e6d9661 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sat, 10 Oct 2020 18:07:47 +0800 Subject: [PATCH 0806/1110] Use `sugg_lint_and_help` --- clippy_lints/src/mut_mutex_lock.rs | 18 +++++++++++------- tests/ui/mut_mutex_lock.fixed | 21 +++++++++++++++++++++ tests/ui/mut_mutex_lock.rs | 2 ++ tests/ui/mut_mutex_lock.stderr | 5 ++--- 4 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 tests/ui/mut_mutex_lock.fixed diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index ca3371a5d75..82ed2d6d69c 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -1,5 +1,6 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -9,7 +10,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `&mut Mutex::lock` calls /// /// **Why is this bad?** `Mutex::lock` is less efficient than - /// calling `Mutex::get_mut` + /// calling `Mutex::get_mut`. In addition you also have a statically + /// guarantee that the mutex isn't locked, instead of just a runtime + /// guarantee. /// /// **Known problems:** None. /// @@ -44,19 +47,20 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &ex.kind; + if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind; if path.ident.name == sym!(lock); let ty = cx.typeck_results().expr_ty(&args[0]); if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { - span_lint_and_help( + span_lint_and_sugg( cx, MUT_MUTEX_LOCK, - ex.span, + *method_span, "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", - None, - "use `&mut Mutex::get_mut` instead", + "change this to", + "get_mut".to_owned(), + Applicability::MachineApplicable, ); } } diff --git a/tests/ui/mut_mutex_lock.fixed b/tests/ui/mut_mutex_lock.fixed new file mode 100644 index 00000000000..36bc52e3374 --- /dev/null +++ b/tests/ui/mut_mutex_lock.fixed @@ -0,0 +1,21 @@ +// run-rustfix +#![allow(dead_code, unused_mut)] +#![warn(clippy::mut_mutex_lock)] + +use std::sync::{Arc, Mutex}; + +fn mut_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + + let mut value = value_mutex.get_mut().unwrap(); + *value += 1; +} + +fn no_owned_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let mut value = value_rc.lock().unwrap(); + *value += 1; +} + +fn main() {} diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 9cd98e90c29..ea60df5ae1b 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -1,3 +1,5 @@ +// run-rustfix +#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index d521ebb56c4..21c1b3486ca 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,11 +1,10 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> $DIR/mut_mutex_lock.rs:9:21 + --> $DIR/mut_mutex_lock.rs:11:33 | LL | let mut value = value_mutex.lock().unwrap(); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^ help: change this to: `get_mut` | = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` - = help: use `&mut Mutex::get_mut` instead error: aborting due to previous error From e7e03b791217645d48e407b28ed023723cedb24c Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sun, 18 Oct 2020 09:09:07 +0800 Subject: [PATCH 0807/1110] Change from correctness to style and MaybeIncorrect instead of MachineApplicable --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/mut_mutex_lock.rs | 4 ++-- src/lintlist/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 648c65ef7b0..9d88cc22d10 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1621,6 +1621,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), @@ -1784,7 +1785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 82ed2d6d69c..df1cecb328c 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// *value += 1; /// ``` pub MUT_MUTEX_LOCK, - correctness, + style, "`&mut Mutex::lock` does unnecessary locking" } @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for MutMutexLock { "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", "change this to", "get_mut".to_owned(), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5e48757a4c5..25ede21da77 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1504,7 +1504,7 @@ vec![ }, Lint { name: "mut_mutex_lock", - group: "correctness", + group: "style", desc: "`&mut Mutex::lock` does unnecessary locking", deprecation: None, module: "mut_mutex_lock", From d5713898acd3e9afe04223522aad50c9da8f05e2 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 25 Oct 2020 19:30:00 +0900 Subject: [PATCH 0808/1110] Remove redundant `expect_local()` call --- clippy_lints/src/functions.rs | 10 ++++------ clippy_lints/src/loops.rs | 3 +-- clippy_lints/src/utils/mod.rs | 2 +- clippy_lints/src/utils/usage.rs | 3 +-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index fd45a6da61c..9c0efef95de 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -579,9 +579,8 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet< if let hir::PatKind::Wild = pat.kind { return false; // ignore `_` patterns } - let def_id = pat.hir_id.owner.to_def_id(); - if cx.tcx.has_typeck_results(def_id) { - is_mutable_ty(cx, &cx.tcx.typeck(def_id.expect_local()).pat_ty(pat), pat.span, tys) + if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { + is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) } else { false } @@ -694,11 +693,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { Call(_, args) | MethodCall(_, _, args, _) => { let mut tys = FxHashSet::default(); for arg in args { - let def_id = arg.hir_id.owner.to_def_id(); - if self.cx.tcx.has_typeck_results(def_id) + if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) && is_mutable_ty( self.cx, - self.cx.tcx.typeck(def_id.expect_local()).expr_ty(arg), + self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), arg.span, &mut tys, ) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 44f2674eaa2..23ca35fffaa 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2051,12 +2051,11 @@ fn check_for_mutation<'tcx>( span_low: None, span_high: None, }; - let def_id = body.hir_id.owner.to_def_id(); cx.tcx.infer_ctxt().enter(|infcx| { ExprUseVisitor::new( &mut delegate, &infcx, - def_id.expect_local(), + body.hir_id.owner, cx.param_env, cx.typeck_results(), ) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a9d26d48b12..8297b9d128d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -299,7 +299,7 @@ pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) - hir::QPath::Resolved(_, path) => path.res, hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { if cx.tcx.has_typeck_results(id.owner.to_def_id()) { - cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id) + cx.tcx.typeck(id.owner).qpath_res(qpath, id) } else { Res::Err } diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 2fd6046ebcf..8b327b2d467 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -19,12 +19,11 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: FxHashSet::default(), skip: false, }; - let def_id = expr.hir_id.owner.to_def_id(); cx.tcx.infer_ctxt().enter(|infcx| { ExprUseVisitor::new( &mut delegate, &infcx, - def_id.expect_local(), + expr.hir_id.owner, cx.param_env, cx.typeck_results(), ) From db8380c4a0e7f707112bbce19a6074a5fac2de59 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Wed, 31 Oct 2018 17:14:55 +0100 Subject: [PATCH 0809/1110] Add lint for unusually-grouped hex/binary literals --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/literal_representation.rs | 42 ++++++++++++++++++++-- clippy_lints/src/utils/numeric_literal.rs | 6 ++-- src/lintlist/mod.rs | 7 ++++ tests/ui/large_digit_groups.fixed | 2 +- tests/ui/large_digit_groups.stderr | 20 +++++++---- tests/ui/literals.rs | 13 ++++--- tests/ui/literals.stderr | 22 +++++++++++- tests/ui/unreadable_literal.fixed | 2 +- tests/ui/unreadable_literal.stderr | 10 +++++- 11 files changed, 107 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd79deb56c..c0dd7b352ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2017,6 +2017,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unusual_byte_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_grouping [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b330b66776c..5d6900f6b96 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,6 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &literal_representation::LARGE_DIGIT_GROUPS, &literal_representation::MISTYPED_LITERAL_SUFFIXES, &literal_representation::UNREADABLE_LITERAL, + &literal_representation::UNUSUAL_BYTE_GROUPING, &loops::EMPTY_LOOP, &loops::EXPLICIT_COUNTER_LOOP, &loops::EXPLICIT_INTO_ITER_LOOP, @@ -1365,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1587,6 +1589,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index c54103b25c2..b41cfe32cfe 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -82,6 +82,25 @@ declare_clippy_lint! { "integer literals with digits grouped inconsistently" } +declare_clippy_lint! { + /// **What it does:** Warns if hexadecimal or binary literals are not grouped + /// by nibble or byte. + /// + /// **Why is this bad?** Negatively impacts readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x: u32 = 0xFFF_FFF; + /// let y: u8 = 0b01_011_101; + /// ``` + pub UNUSUAL_BYTE_GROUPING, + style, + "binary or hex literals that aren't grouped by four" +} + declare_clippy_lint! { /// **What it does:** Warns if the digits of an integral or floating-point /// constant are grouped into groups that @@ -125,6 +144,7 @@ enum WarningType { LargeDigitGroups, DecimalRepresentation, MistypedLiteralSuffix, + UnusualByteGrouping, } impl WarningType { @@ -175,6 +195,15 @@ impl WarningType { suggested_format, Applicability::MachineApplicable, ), + Self::UnusualByteGrouping => span_lint_and_sugg( + cx, + UNUSUAL_BYTE_GROUPING, + span, + "digits of hex or binary literal not grouped by four", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), }; } } @@ -184,6 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [ INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, + UNUSUAL_BYTE_GROUPING, ]); impl EarlyLintPass for LiteralDigitGrouping { @@ -217,9 +247,9 @@ impl LiteralDigitGrouping { let result = (|| { - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?; + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?; if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?; + let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?; let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, @@ -229,6 +259,7 @@ impl LiteralDigitGrouping { return Err(WarningType::InconsistentDigitGrouping); }; } + Ok(()) })(); @@ -237,6 +268,7 @@ impl LiteralDigitGrouping { let should_warn = match warning_type { | WarningType::UnreadableLiteral | WarningType::InconsistentDigitGrouping + | WarningType::UnusualByteGrouping | WarningType::LargeDigitGroups => { !in_macro(lit.span) } @@ -331,11 +363,15 @@ impl LiteralDigitGrouping { /// Returns the size of the digit groups (or None if ungrouped) if successful, /// otherwise returns a `WarningType` for linting. - fn get_group_size<'a>(groups: impl Iterator) -> Result, WarningType> { + fn get_group_size<'a>(groups: impl Iterator, radix: Radix) -> Result, WarningType> { let mut groups = groups.map(str::len); let first = groups.next().expect("At least one group"); + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i % 4 != 0) { + return Err(WarningType::UnusualByteGrouping); + } + if let Some(second) = groups.next() { if !groups.all(|x| x == second) || first > second { Err(WarningType::InconsistentDigitGrouping) diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 52d3c2c1daf..d02603d7702 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -1,6 +1,6 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum Radix { Binary, Octal, @@ -11,8 +11,8 @@ pub enum Radix { impl Radix { /// Returns a reasonable digit group size for this radix. #[must_use] - fn suggest_grouping(&self) -> usize { - match *self { + fn suggest_grouping(self) -> usize { + match self { Self::Binary | Self::Hexadecimal => 4, Self::Octal | Self::Decimal => 3, } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4bf77dae637..fc8efb81cfc 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2643,6 +2643,13 @@ vec![ deprecation: None, module: "unused_unit", }, + Lint { + name: "unusual_byte_grouping", + group: "style", + desc: "binary or hex literals that aren\'t grouped by four", + deprecation: None, + module: "literal_representation", + }, Lint { name: "unwrap_in_result", group: "restriction", diff --git a/tests/ui/large_digit_groups.fixed b/tests/ui/large_digit_groups.fixed index 859fad2f54d..3430c137ec2 100644 --- a/tests/ui/large_digit_groups.fixed +++ b/tests/ui/large_digit_groups.fixed @@ -11,7 +11,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x1_234_567, + 0x0123_4567, 1_2345_6789, 1234_f32, 1_234.12_f32, diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index b6d9672a78e..fe472e66949 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -1,24 +1,30 @@ -error: digit groups should be smaller +error: digits of hex or binary literal not grouped by four + --> $DIR/large_digit_groups.rs:14:9 + | +LL | 0x1_234_567, + | ^^^^^^^^^^^ help: consider: `0x0123_4567` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + +error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:22:9 | LL | 0b1_10110_i64, | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64` - | - = note: `-D clippy::large-digit-groups` implied by `-D warnings` -error: digits grouped inconsistently by underscores +error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:23:9 | LL | 0xd_e_adbee_f_usize, | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` - | - = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:24:9 | LL | 1_23456_f32, | ^^^^^^^^^^^ help: consider: `123_456_f32` + | + = note: `-D clippy::large-digit-groups` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:25:9 @@ -38,5 +44,5 @@ error: digit groups should be smaller LL | 1_23456.12345_6_f64, | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index c299b16c8ce..2608638ff80 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,10 +7,10 @@ fn main() { let ok1 = 0xABCD; - let ok3 = 0xab_cd; - let ok4 = 0xab_cd_i32; - let ok5 = 0xAB_CD_u32; - let ok5 = 0xAB_CD_isize; + let ok3 = 0xabcd; + let ok4 = 0xabcd_i32; + let ok5 = 0xABCD_u32; + let ok5 = 0xABCD_isize; let fail1 = 0xabCD; let fail2 = 0xabCD_u32; let fail2 = 0xabCD_isize; @@ -33,4 +33,9 @@ fn main() { let fail19 = 12_3456_21; let fail22 = 3__4___23; let fail23 = 3__16___23; + + let fail24 = 0xAB_ABC_AB; + let fail25 = 0b01_100_101; + let ok26 = 0x6_A0_BF; + let ok27 = 0b1_0010_0101; } diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 0b3af2d8bc3..c88848a40fc 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -69,5 +69,25 @@ error: digits grouped inconsistently by underscores LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` -error: aborting due to 8 previous errors +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:37:18 + | +LL | let fail24 = 0xAB_ABC_AB; + | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:38:18 + | +LL | let fail25 = 0b01_100_101; + | ^^^^^^^^^^^^ help: consider: `0b0110_0101` + +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:39:16 + | +LL | let ok26 = 0x6_A0_BF; + | ^^^^^^^^^ help: consider: `0x0006_A0BF` + +error: aborting due to 11 previous errors diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 3f358d9ecaa..4043d53299f 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -14,7 +14,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x1_234_567, + 0x0123_4567, 65536, 1_2345_6789, 1234_f32, diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 1b2ff6bff04..fa4c3fe13e3 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,3 +1,11 @@ +error: digits of hex or binary literal not grouped by four + --> $DIR/unreadable_literal.rs:17:9 + | +LL | 0x1_234_567, + | ^^^^^^^^^^^ help: consider: `0x0123_4567` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + error: long literal lacking separators --> $DIR/unreadable_literal.rs:25:17 | @@ -54,5 +62,5 @@ error: long literal lacking separators LL | let _fail12: i128 = 0xabcabcabcabcabcabc; | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors From f5a88b6de558d17ed088a93756facb901c7c0888 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 09:18:06 -0400 Subject: [PATCH 0810/1110] Allow hex literals to pass w/ groups of 2 --- clippy_lints/src/literal_representation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b41cfe32cfe..813f3b6f378 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -368,7 +368,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i % 4 != 0) { + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 || i != 2) { return Err(WarningType::UnusualByteGrouping); } From 0c0f8db347e406682875fcd08d2bc483e93f710f Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 09:19:58 -0400 Subject: [PATCH 0811/1110] Remove accidental test inclusion --- tests/ui/literals.stderr | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index c88848a40fc..e321f2a1cef 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -83,11 +83,5 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: digits of hex or binary literal not grouped by four - --> $DIR/literals.rs:39:16 - | -LL | let ok26 = 0x6_A0_BF; - | ^^^^^^^^^ help: consider: `0x0006_A0BF` - -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors From e7e4b35bdf695c4db5a1f34319d01b12a9d54b34 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 09:34:46 -0400 Subject: [PATCH 0812/1110] Fix logic mistake --- clippy_lints/src/literal_representation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 813f3b6f378..f51a0edc9c5 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -368,7 +368,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 || i != 2) { + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) { return Err(WarningType::UnusualByteGrouping); } From 3ce820bf83657879493aa7b107634f1951e7c219 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 21 Oct 2020 22:33:41 +0900 Subject: [PATCH 0813/1110] Fix an invalid suggestion in `needless_collect` test --- clippy_lints/src/loops.rs | 9 ++++++++- tests/ui/needless_collect_indirect.stderr | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 23ca35fffaa..c3f75f283f4 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3001,7 +3001,14 @@ impl IterFunction { IterFunctionKind::IntoIter => String::new(), IterFunctionKind::Len => String::from(".count()"), IterFunctionKind::IsEmpty => String::from(".next().is_none()"), - IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")), + IterFunctionKind::Contains(span) => { + let s = snippet(cx, *span, ".."); + if let Some(stripped) = s.strip_prefix('&') { + format!(".any(|x| x == {})", stripped) + } else { + format!(".any(|x| x == *{})", s) + } + }, } } fn get_suggestion_text(&self) -> &'static str { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 0c1e61d7496..7b8e227f304 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -48,7 +48,7 @@ LL | | indirect_contains.contains(&&5); help: Check if the original Iterator contains an element instead of collecting then checking | LL | -LL | sample.iter().any(|x| x == &&5); +LL | sample.iter().any(|x| x == &5); | error: aborting due to 4 previous errors From 2f5d418011f80d99e0e4c8ddea3980b6cdc8332d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 25 Oct 2020 23:55:41 +0900 Subject: [PATCH 0814/1110] Add test case --- tests/ui/needless_collect_indirect.rs | 6 ++++++ tests/ui/needless_collect_indirect.stderr | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 4cf03e82035..4f6e5357727 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -16,4 +16,10 @@ fn main() { .into_iter() .map(|x| (*x, *x + 1)) .collect::>(); + + // #6202 + let a = "a".to_string(); + let sample = vec![a.clone(), "b".to_string(), "c".to_string()]; + let non_copy_contains = sample.into_iter().collect::>(); + non_copy_contains.contains(&a); } diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 7b8e227f304..fb807da5f8a 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -51,5 +51,18 @@ LL | LL | sample.iter().any(|x| x == &5); | -error: aborting due to 4 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:23:5 + | +LL | / let non_copy_contains = sample.into_iter().collect::>(); +LL | | non_copy_contains.contains(&a); + | |____^ + | +help: Check if the original Iterator contains an element instead of collecting then checking + | +LL | +LL | sample.into_iter().any(|x| x == a); + | + +error: aborting due to 5 previous errors From 312bbff6968dbebd367ca90677c676e2cf5198d2 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 11:31:24 -0400 Subject: [PATCH 0815/1110] Integrate suggestions from code review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +++--- clippy_lints/src/literal_representation.rs | 14 +++++++------- src/lintlist/mod.rs | 2 +- tests/ui/large_digit_groups.stderr | 2 +- tests/ui/literals.rs | 8 ++++---- tests/ui/literals.stderr | 2 +- tests/ui/unreadable_literal.stderr | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0dd7b352ad..25f3b5da198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2017,7 +2017,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit -[`unusual_byte_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_grouping +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5d6900f6b96..3be8bc0e36d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,7 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &literal_representation::LARGE_DIGIT_GROUPS, &literal_representation::MISTYPED_LITERAL_SUFFIXES, &literal_representation::UNREADABLE_LITERAL, - &literal_representation::UNUSUAL_BYTE_GROUPING, + &literal_representation::UNUSUAL_BYTE_GROUPINGS, &loops::EMPTY_LOOP, &loops::EXPLICIT_COUNTER_LOOP, &loops::EXPLICIT_INTO_ITER_LOOP, @@ -1366,7 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1589,7 +1589,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index f51a0edc9c5..e8a741683da 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -96,7 +96,7 @@ declare_clippy_lint! { /// let x: u32 = 0xFFF_FFF; /// let y: u8 = 0b01_011_101; /// ``` - pub UNUSUAL_BYTE_GROUPING, + pub UNUSUAL_BYTE_GROUPINGS, style, "binary or hex literals that aren't grouped by four" } @@ -144,7 +144,7 @@ enum WarningType { LargeDigitGroups, DecimalRepresentation, MistypedLiteralSuffix, - UnusualByteGrouping, + UnusualByteGroupings, } impl WarningType { @@ -195,9 +195,9 @@ impl WarningType { suggested_format, Applicability::MachineApplicable, ), - Self::UnusualByteGrouping => span_lint_and_sugg( + Self::UnusualByteGroupings => span_lint_and_sugg( cx, - UNUSUAL_BYTE_GROUPING, + UNUSUAL_BYTE_GROUPINGS, span, "digits of hex or binary literal not grouped by four", "consider", @@ -213,7 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [ INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, - UNUSUAL_BYTE_GROUPING, + UNUSUAL_BYTE_GROUPINGS, ]); impl EarlyLintPass for LiteralDigitGrouping { @@ -268,7 +268,7 @@ impl LiteralDigitGrouping { let should_warn = match warning_type { | WarningType::UnreadableLiteral | WarningType::InconsistentDigitGrouping - | WarningType::UnusualByteGrouping + | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { !in_macro(lit.span) } @@ -369,7 +369,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) { - return Err(WarningType::UnusualByteGrouping); + return Err(WarningType::UnusualByteGroupings); } if let Some(second) = groups.next() { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index fc8efb81cfc..6272ce45efb 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2644,7 +2644,7 @@ vec![ module: "unused_unit", }, Lint { - name: "unusual_byte_grouping", + name: "unusual_byte_groupings", group: "style", desc: "binary or hex literals that aren\'t grouped by four", deprecation: None, diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index fe472e66949..13d108b56e0 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -4,7 +4,7 @@ error: digits of hex or binary literal not grouped by four LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:22:9 diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 2608638ff80..a72a74b9131 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,10 +7,10 @@ fn main() { let ok1 = 0xABCD; - let ok3 = 0xabcd; - let ok4 = 0xabcd_i32; - let ok5 = 0xABCD_u32; - let ok5 = 0xABCD_isize; + let ok3 = 0xab_cd; + let ok4 = 0xab_cd_i32; + let ok5 = 0xAB_CD_u32; + let ok5 = 0xAB_CD_isize; let fail1 = 0xabCD; let fail2 = 0xabCD_u32; let fail2 = 0xabCD_isize; diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index e321f2a1cef..64ceeb316d8 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -75,7 +75,7 @@ error: digits of hex or binary literal not grouped by four LL | let fail24 = 0xAB_ABC_AB; | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digits of hex or binary literal not grouped by four --> $DIR/literals.rs:38:18 diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index fa4c3fe13e3..8645cabeabb 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -4,7 +4,7 @@ error: digits of hex or binary literal not grouped by four LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: long literal lacking separators --> $DIR/unreadable_literal.rs:25:17 From 80b21682bf9d2e10d4daf44ae5f6c4965950dac9 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sun, 25 Oct 2020 21:34:46 +0100 Subject: [PATCH 0816/1110] Update triagebot.toml --- triagebot.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index ed3c83af616..b7b20b40e68 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ - "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", "S-*", - "good first issue", "needs test" + "A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*", + "good first issue" ] [assign] From de5a6d3420d9f2f2c4c995e325412e862a9dc583 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 00:31:25 +0100 Subject: [PATCH 0817/1110] Initial implementation of comparison_to_empty --- CHANGELOG.md | 1 + Cargo.toml | 7 ++ clippy_lints/Cargo.toml | 22 ++++ clippy_lints/src/comparison_to_empty.rs | 155 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + src/lintlist/mod.rs | 7 ++ tests/ui/comparison_to_empty.fixed | 23 ++++ tests/ui/comparison_to_empty.rs | 23 ++++ tests/ui/comparison_to_empty.stderr | 28 +++++ 9 files changed, 271 insertions(+) create mode 100644 clippy_lints/src/comparison_to_empty.rs create mode 100644 tests/ui/comparison_to_empty.fixed create mode 100644 tests/ui/comparison_to_empty.rs create mode 100644 tests/ui/comparison_to_empty.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f96398153..869e9949167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1664,6 +1664,7 @@ Released 2018-09-13 [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute diff --git a/Cargo.toml b/Cargo.toml index 1ddcd18598d..dae3e03074f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,13 @@ path = "src/main.rs" name = "clippy-driver" path = "src/driver.rs" +[target.'cfg(NOT_A_PLATFORM)'.dependencies] +rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } +rustc_driver = { path = "/home/urcra/rust/compiler/rustc_driver" } +rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } +rustc_interface = { path = "/home/urcra/rust/compiler/rustc_interface" } +rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } + [dependencies] # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..167db15be20 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -16,6 +16,28 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] edition = "2018" +[target.'cfg(NOT_A_PLATFORM)'.dependencies] +rustc_ast = { path = "/home/urcra/rust/compiler/rustc_ast" } +rustc_ast_pretty = { path = "/home/urcra/rust/compiler/rustc_ast_pretty" } +rustc_attr = { path = "/home/urcra/rust/compiler/rustc_attr" } +rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } +rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } +rustc_hir = { path = "/home/urcra/rust/compiler/rustc_hir" } +rustc_hir_pretty = { path = "/home/urcra/rust/compiler/rustc_hir_pretty" } +rustc_index = { path = "/home/urcra/rust/compiler/rustc_index" } +rustc_infer = { path = "/home/urcra/rust/compiler/rustc_infer" } +rustc_lexer = { path = "/home/urcra/rust/compiler/rustc_lexer" } +rustc_lint = { path = "/home/urcra/rust/compiler/rustc_lint" } +rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } +rustc_mir = { path = "/home/urcra/rust/compiler/rustc_mir" } +rustc_parse = { path = "/home/urcra/rust/compiler/rustc_parse" } +rustc_parse_format = { path = "/home/urcra/rust/compiler/rustc_parse_format" } +rustc_session = { path = "/home/urcra/rust/compiler/rustc_session" } +rustc_span = { path = "/home/urcra/rust/compiler/rustc_span" } +rustc_target = { path = "/home/urcra/rust/compiler/rustc_target" } +rustc_trait_selection = { path = "/home/urcra/rust/compiler/rustc_trait_selection" } +rustc_typeck = { path = "/home/urcra/rust/compiler/rustc_typeck" } + [dependencies] cargo_metadata = "0.12" if_chain = "1.0.0" diff --git a/clippy_lints/src/comparison_to_empty.rs b/clippy_lints/src/comparison_to_empty.rs new file mode 100644 index 00000000000..3b55336722c --- /dev/null +++ b/clippy_lints/src/comparison_to_empty.rs @@ -0,0 +1,155 @@ +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, ItemKind, TraitItemRef}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{Span, Spanned}; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub COMPARISON_TO_EMPTY, + style, + "default lint description" +} + +declare_lint_pass!(ComparisonToEmpty => [COMPARISON_TO_EMPTY]); + +impl LateLintPass<'_> for ComparisonToEmpty { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + match cmp { + BinOpKind::Eq => { + check_cmp(cx, expr.span, left, right, "", 0); // len == 0 + check_cmp(cx, expr.span, right, left, "", 0); // 0 == len + }, + BinOpKind::Ne => { + check_cmp(cx, expr.span, left, right, "!", 0); // len != 0 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len + }, + BinOpKind::Gt => { + check_cmp(cx, expr.span, left, right, "!", 0); // len > 0 + check_cmp(cx, expr.span, right, left, "", 1); // 1 > len + }, + BinOpKind::Lt => { + check_cmp(cx, expr.span, left, right, "", 1); // len < 1 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len + }, + BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1 + BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len + _ => (), + } + } + } + +} + + +fn check_cmp(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str, compare_to: u32) { + check_empty_expr(cx, span, lit1, lit2, op) +} + +fn check_empty_expr( + cx: &LateContext<'_>, + span: Span, + lit1: &Expr<'_>, + lit2: &Expr<'_>, + op: &str +) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + &format!("comparison to empty slice"), + &format!("using `{}is_empty` is clearer and more explicit", op), + format!( + "{}{}.is_empty()", + op, + snippet_with_applicability(cx, lit1.span, "_", &mut applicability) + ), + applicability, + ); + } +} + +fn is_empty_string(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(lit, _) = lit.node { + let lit = lit.as_str(); + return lit == ""; + } + } + false +} + +fn is_empty_array(expr: &Expr<'_>) -> bool { + if let ExprKind::Array(ref arr) = expr.kind { + return arr.is_empty(); + } + false +} + + +/// Checks if this type has an `is_empty` method. +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. + fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { + if let ty::AssocKind::Fn = item.kind { + if item.ident.name.as_str() == "is_empty" { + let sig = cx.tcx.fn_sig(item.def_id); + let ty = sig.skip_binder(); + ty.inputs().len() == 1 + } else { + false + } + } else { + false + } + } + + /// Checks the inherent impl's items for an `is_empty(self)` method. + fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + cx.tcx.inherent_impls(id).iter().any(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }) + } + + let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); + match ty.kind() { + ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + cx.tcx + .associated_items(principal.def_id()) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }), + ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), + ty::Adt(id, _) => has_is_empty_impl(cx, id.did), + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1a950a7c334..75629e13a8e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,6 +171,7 @@ mod checked_conversions; mod cognitive_complexity; mod collapsible_if; mod comparison_chain; +mod comparison_to_empty; mod copies; mod copy_iterator; mod create_dir; @@ -523,6 +524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, + &comparison_to_empty::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -1139,6 +1141,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_late_pass(|| box comparison_to_empty::ComparisonToEmpty); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1299,6 +1302,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1555,6 +1559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index da865a66c45..c2b2236bc66 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -291,6 +291,13 @@ vec![ deprecation: None, module: "comparison_chain", }, + Lint { + name: "comparison_to_empty", + group: "style", + desc: "default lint description", + deprecation: None, + module: "comparison_to_empty", + }, Lint { name: "copy_iterator", group: "pedantic", diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed new file mode 100644 index 00000000000..261024caca7 --- /dev/null +++ b/tests/ui/comparison_to_empty.fixed @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::comparison_to_empty)] + +fn main() { + // Disallow comparisons to empty + let s = String::new(); + let _ = s.is_empty(); + let _ = !s.is_empty(); + + let v = vec![0]; + let _ = v.is_empty(); + let _ = !v.is_empty(); + + // Allow comparisons to non-empty + let s = String::new(); + let _ = s == " "; + let _ = s != " "; + + let v = vec![0]; + let _ = v == [0]; + let _ = v != [0]; +} diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs new file mode 100644 index 00000000000..98ddd974951 --- /dev/null +++ b/tests/ui/comparison_to_empty.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::comparison_to_empty)] + +fn main() { + // Disallow comparisons to empty + let s = String::new(); + let _ = s == ""; + let _ = s != ""; + + let v = vec![0]; + let _ = v == []; + let _ = v != []; + + // Allow comparisons to non-empty + let s = String::new(); + let _ = s == " "; + let _ = s != " "; + + let v = vec![0]; + let _ = v == [0]; + let _ = v != [0]; +} diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr new file mode 100644 index 00000000000..f69d6bd5255 --- /dev/null +++ b/tests/ui/comparison_to_empty.stderr @@ -0,0 +1,28 @@ +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:8:13 + | +LL | let _ = s == ""; + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + | + = note: `-D clippy::comparison-to-empty` implied by `-D warnings` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:9:13 + | +LL | let _ = s != ""; + | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:12:13 + | +LL | let _ = v == []; + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:13:13 + | +LL | let _ = v != []; + | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` + +error: aborting due to 4 previous errors + From 7f7faa1ccf4683582563d2f7a63aba988cb24cc8 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 00:51:18 +0100 Subject: [PATCH 0818/1110] Move implementation into len_zero.rs --- clippy_lints/src/comparison_to_empty.rs | 155 ------------------------ clippy_lints/src/len_zero.rs | 84 ++++++++++++- clippy_lints/src/lib.rs | 8 +- 3 files changed, 86 insertions(+), 161 deletions(-) delete mode 100644 clippy_lints/src/comparison_to_empty.rs diff --git a/clippy_lints/src/comparison_to_empty.rs b/clippy_lints/src/comparison_to_empty.rs deleted file mode 100644 index 3b55336722c..00000000000 --- a/clippy_lints/src/comparison_to_empty.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, Expr, ExprKind, ItemKind, TraitItemRef}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned}; - -declare_clippy_lint! { - /// **What it does:** - /// - /// **Why is this bad?** - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub COMPARISON_TO_EMPTY, - style, - "default lint description" -} - -declare_lint_pass!(ComparisonToEmpty => [COMPARISON_TO_EMPTY]); - -impl LateLintPass<'_> for ComparisonToEmpty { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { - match cmp { - BinOpKind::Eq => { - check_cmp(cx, expr.span, left, right, "", 0); // len == 0 - check_cmp(cx, expr.span, right, left, "", 0); // 0 == len - }, - BinOpKind::Ne => { - check_cmp(cx, expr.span, left, right, "!", 0); // len != 0 - check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len - }, - BinOpKind::Gt => { - check_cmp(cx, expr.span, left, right, "!", 0); // len > 0 - check_cmp(cx, expr.span, right, left, "", 1); // 1 > len - }, - BinOpKind::Lt => { - check_cmp(cx, expr.span, left, right, "", 1); // len < 1 - check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len - }, - BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1 - BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len - _ => (), - } - } - } - -} - - -fn check_cmp(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str, compare_to: u32) { - check_empty_expr(cx, span, lit1, lit2, op) -} - -fn check_empty_expr( - cx: &LateContext<'_>, - span: Span, - lit1: &Expr<'_>, - lit2: &Expr<'_>, - op: &str -) { - if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - COMPARISON_TO_EMPTY, - span, - &format!("comparison to empty slice"), - &format!("using `{}is_empty` is clearer and more explicit", op), - format!( - "{}{}.is_empty()", - op, - snippet_with_applicability(cx, lit1.span, "_", &mut applicability) - ), - applicability, - ); - } -} - -fn is_empty_string(expr: &Expr<'_>) -> bool { - if let ExprKind::Lit(ref lit) = expr.kind { - if let LitKind::Str(lit, _) = lit.node { - let lit = lit.as_str(); - return lit == ""; - } - } - false -} - -fn is_empty_array(expr: &Expr<'_>) -> bool { - if let ExprKind::Array(ref arr) = expr.kind { - return arr.is_empty(); - } - false -} - - -/// Checks if this type has an `is_empty` method. -fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. - fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if let ty::AssocKind::Fn = item.kind { - if item.ident.name.as_str() == "is_empty" { - let sig = cx.tcx.fn_sig(item.def_id); - let ty = sig.skip_binder(); - ty.inputs().len() == 1 - } else { - false - } - } else { - false - } - } - - /// Checks the inherent impl's items for an `is_empty(self)` method. - fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { - cx.tcx.inherent_impls(id).iter().any(|imp| { - cx.tcx - .associated_items(*imp) - .in_definition_order() - .any(|item| is_is_empty(cx, &item)) - }) - } - - let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); - match ty.kind() { - ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { - cx.tcx - .associated_items(principal.def_id()) - .in_definition_order() - .any(|item| is_is_empty(cx, &item)) - }), - ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), - ty::Adt(id, _) => has_is_empty_impl(cx, id.did), - ty::Array(..) | ty::Slice(..) | ty::Str => true, - _ => false, - } -} diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index c9c4891bb08..75e9c5c973b 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -68,7 +68,45 @@ declare_clippy_lint! { "traits or impls with a public `len` method but no corresponding `is_empty` method" } -declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]); +declare_clippy_lint! { + /// **What it does:** Checks for comparing to an empty slice such as "" or [],` + /// and suggests using `.is_empty()` where applicable. + /// + /// **Why is this bad?** Some structures can answer `.is_empty()` much faster + /// than checking for equality. So it is good to get into the habit of using + /// `.is_empty()`, and having it is cheap. + /// Besides, it makes the intent clearer than a manual comparison in some contexts. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```ignore + /// if s == "" { + /// .. + /// } + + /// if arr == [] { + /// .. + /// } + /// ``` + /// Use instead: + /// ```ignore + /// if s.is_empty() { + /// .. + /// } + + /// if arr.is_empty() { + /// .. + /// } + /// ``` + pub COMPARISON_TO_EMPTY, + style, + "default lint description" +} + + +declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); impl<'tcx> LateLintPass<'tcx> for LenZero { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -221,6 +259,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> } check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to) + } else { + check_empty_expr(cx, span, method, lit, op) } } @@ -258,6 +298,48 @@ fn check_len( } } +fn check_empty_expr( + cx: &LateContext<'_>, + span: Span, + lit1: &Expr<'_>, + lit2: &Expr<'_>, + op: &str +) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + &format!("comparison to empty slice"), + &format!("using `{}is_empty` is clearer and more explicit", op), + format!( + "{}{}.is_empty()", + op, + snippet_with_applicability(cx, lit1.span, "_", &mut applicability) + ), + applicability, + ); + } +} + +fn is_empty_string(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(lit, _) = lit.node { + let lit = lit.as_str(); + return lit == ""; + } + } + false +} + +fn is_empty_array(expr: &Expr<'_>) -> bool { + if let ExprKind::Array(ref arr) = expr.kind { + return arr.is_empty(); + } + false +} + /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 75629e13a8e..3ad2e1e9d57 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,7 +171,6 @@ mod checked_conversions; mod cognitive_complexity; mod collapsible_if; mod comparison_chain; -mod comparison_to_empty; mod copies; mod copy_iterator; mod create_dir; @@ -524,7 +523,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, - &comparison_to_empty::COMPARISON_TO_EMPTY, + &len_zero::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -1141,7 +1140,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); - store.register_late_pass(|| box comparison_to_empty::ComparisonToEmpty); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1302,7 +1300,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1559,7 +1557,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), From 4417af801d8a9cd06da2d1986d8a808dea2d8233 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 00:53:51 +0100 Subject: [PATCH 0819/1110] Revert changes to Cargo.toml file --- Cargo.toml | 7 ------- clippy_lints/Cargo.toml | 22 ---------------------- 2 files changed, 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dae3e03074f..1ddcd18598d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,13 +27,6 @@ path = "src/main.rs" name = "clippy-driver" path = "src/driver.rs" -[target.'cfg(NOT_A_PLATFORM)'.dependencies] -rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } -rustc_driver = { path = "/home/urcra/rust/compiler/rustc_driver" } -rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } -rustc_interface = { path = "/home/urcra/rust/compiler/rustc_interface" } -rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } - [dependencies] # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 167db15be20..d9471d25197 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -16,28 +16,6 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] edition = "2018" -[target.'cfg(NOT_A_PLATFORM)'.dependencies] -rustc_ast = { path = "/home/urcra/rust/compiler/rustc_ast" } -rustc_ast_pretty = { path = "/home/urcra/rust/compiler/rustc_ast_pretty" } -rustc_attr = { path = "/home/urcra/rust/compiler/rustc_attr" } -rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } -rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } -rustc_hir = { path = "/home/urcra/rust/compiler/rustc_hir" } -rustc_hir_pretty = { path = "/home/urcra/rust/compiler/rustc_hir_pretty" } -rustc_index = { path = "/home/urcra/rust/compiler/rustc_index" } -rustc_infer = { path = "/home/urcra/rust/compiler/rustc_infer" } -rustc_lexer = { path = "/home/urcra/rust/compiler/rustc_lexer" } -rustc_lint = { path = "/home/urcra/rust/compiler/rustc_lint" } -rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } -rustc_mir = { path = "/home/urcra/rust/compiler/rustc_mir" } -rustc_parse = { path = "/home/urcra/rust/compiler/rustc_parse" } -rustc_parse_format = { path = "/home/urcra/rust/compiler/rustc_parse_format" } -rustc_session = { path = "/home/urcra/rust/compiler/rustc_session" } -rustc_span = { path = "/home/urcra/rust/compiler/rustc_span" } -rustc_target = { path = "/home/urcra/rust/compiler/rustc_target" } -rustc_trait_selection = { path = "/home/urcra/rust/compiler/rustc_trait_selection" } -rustc_typeck = { path = "/home/urcra/rust/compiler/rustc_typeck" } - [dependencies] cargo_metadata = "0.12" if_chain = "1.0.0" From deecfb5d13325d05842fb610a7ef506be4ac6175 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:02:10 +0100 Subject: [PATCH 0820/1110] Add description to lint --- clippy_lints/src/len_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 75e9c5c973b..9eba3750d2a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -102,7 +102,7 @@ declare_clippy_lint! { /// ``` pub COMPARISON_TO_EMPTY, style, - "default lint description" + "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead" } From 4cf5b5ff7018abc91d0d0a81c73b3405228811a9 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:21:34 +0100 Subject: [PATCH 0821/1110] Run update lints --- clippy_lints/src/lib.rs | 6 +++--- src/lintlist/mod.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3ad2e1e9d57..8dff5ae42c3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -523,7 +523,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, - &len_zero::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -609,6 +608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_const_arrays::LARGE_CONST_ARRAYS, &large_enum_variant::LARGE_ENUM_VARIANT, &large_stack_arrays::LARGE_STACK_ARRAYS, + &len_zero::COMPARISON_TO_EMPTY, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, &let_if_seq::USELESS_LET_IF_SEQ, @@ -1300,7 +1300,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1350,6 +1349,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&int_plus_one::INT_PLUS_ONE), LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), @@ -1557,7 +1557,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), @@ -1573,6 +1572,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&inherent_to_string::INHERENT_TO_STRING), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c2b2236bc66..695c7408e23 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -294,9 +294,9 @@ vec![ Lint { name: "comparison_to_empty", group: "style", - desc: "default lint description", + desc: "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead", deprecation: None, - module: "comparison_to_empty", + module: "len_zero", }, Lint { name: "copy_iterator", From 4532dd9e431e3df719941514ef2a3dbce7fff962 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:31:13 +0100 Subject: [PATCH 0822/1110] run cargo fmt --- clippy_lints/src/len_zero.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 9eba3750d2a..542b4afcdf9 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -105,7 +105,6 @@ declare_clippy_lint! { "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead" } - declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); impl<'tcx> LateLintPass<'tcx> for LenZero { @@ -298,13 +297,7 @@ fn check_len( } } -fn check_empty_expr( - cx: &LateContext<'_>, - span: Span, - lit1: &Expr<'_>, - lit2: &Expr<'_>, - op: &str -) { +fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( From 45aa3ef8dde9741c82bd3c98405d0b0464a8b79f Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:55:44 +0100 Subject: [PATCH 0823/1110] Remove unnecesary format --- clippy_lints/src/len_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 542b4afcdf9..d0e7dea4a54 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -304,7 +304,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex cx, COMPARISON_TO_EMPTY, span, - &format!("comparison to empty slice"), + "comparison to empty slice", &format!("using `{}is_empty` is clearer and more explicit", op), format!( "{}{}.is_empty()", From f0cf3bdca198ead0e1d76115bf30a2eef72e8c58 Mon Sep 17 00:00:00 2001 From: HMPerson1 Date: Sun, 25 Oct 2020 21:20:57 -0400 Subject: [PATCH 0824/1110] Add lint for replacing `.map().collect()` with `.try_for_each()` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 59 +++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/map_collect_result_unit.fixed | 16 +++++++ tests/ui/map_collect_result_unit.rs | 16 +++++++ tests/ui/map_collect_result_unit.stderr | 16 +++++++ 7 files changed, 118 insertions(+) create mode 100644 tests/ui/map_collect_result_unit.fixed create mode 100644 tests/ui/map_collect_result_unit.rs create mode 100644 tests/ui/map_collect_result_unit.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..287cf6bb725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1802,6 +1802,7 @@ Released 2018-09-13 [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..138d1ab9b50 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -697,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, + &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, @@ -1420,6 +1421,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OPTION_AS_REF_DEREF), @@ -1615,6 +1617,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OPTION_MAP_OR_NONE), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0824bacbc7..5f9bed90845 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1383,6 +1383,27 @@ declare_clippy_lint! { "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map(_).collect::()`. + /// + /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// (0..3).map(|t| Err(t)).collect::>(); + /// ``` + /// Use instead: + /// ```rust + /// (0..3).try_for_each(|t| Err(t)); + /// ``` + pub MAP_COLLECT_RESULT_UNIT, + style, + "using `.map(_).collect::()`, which can be replaced with `try_for_each`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1433,6 +1454,7 @@ declare_lint_pass!(Methods => [ FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, + MAP_COLLECT_RESULT_UNIT, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1515,6 +1537,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), + ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), _ => {}, } @@ -3501,6 +3524,42 @@ fn lint_option_as_ref_deref<'tcx>( } } +fn lint_map_collect( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + map_args: &[hir::Expr<'_>], + collect_args: &[hir::Expr<'_>], +) { + if_chain! { + // called on Iterator + if let [map_expr] = collect_args; + if match_trait_method(cx, map_expr, &paths::ITERATOR); + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, collect_ret_ty, sym!(result_type)); + if let ty::Adt(_, substs) = collect_ret_ty.kind(); + if let Some(result_t) = substs.types().next(); + if result_t.is_unit(); + // get parts for snippet + if let [iter, map_fn] = map_args; + then { + span_lint_and_sugg( + cx, + MAP_COLLECT_RESULT_UNIT, + expr.span, + "`.map().collect()` can be replaced with `.try_for_each()`", + "try this", + format!( + "{}.try_for_each({})", + snippet(cx, iter.span, ".."), + snippet(cx, map_fn.span, "..") + ), + Applicability::MachineApplicable, + ); + } + } +} + /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..d9b3e5c17f4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1222,6 +1222,13 @@ vec![ deprecation: None, module: "map_clone", }, + Lint { + name: "map_collect_result_unit", + group: "style", + desc: "using `.map(_).collect::()`, which can be replaced with `try_for_each`", + deprecation: None, + module: "methods", + }, Lint { name: "map_entry", group: "perf", diff --git a/tests/ui/map_collect_result_unit.fixed b/tests/ui/map_collect_result_unit.fixed new file mode 100644 index 00000000000..e66c9cc2420 --- /dev/null +++ b/tests/ui/map_collect_result_unit.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::map_collect_result_unit)] + +fn main() { + { + let _ = (0..3).try_for_each(|t| Err(t + 1)); + let _: Result<(), _> = (0..3).try_for_each(|t| Err(t + 1)); + + let _ = (0..3).try_for_each(|t| Err(t + 1)); + } +} + +fn _ignore() { + let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/tests/ui/map_collect_result_unit.rs b/tests/ui/map_collect_result_unit.rs new file mode 100644 index 00000000000..6f08f4c3c53 --- /dev/null +++ b/tests/ui/map_collect_result_unit.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::map_collect_result_unit)] + +fn main() { + { + let _ = (0..3).map(|t| Err(t + 1)).collect::>(); + let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); + + let _ = (0..3).try_for_each(|t| Err(t + 1)); + } +} + +fn _ignore() { + let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr new file mode 100644 index 00000000000..8b06e13baa6 --- /dev/null +++ b/tests/ui/map_collect_result_unit.stderr @@ -0,0 +1,16 @@ +error: `.map().collect()` can be replaced with `.try_for_each()` + --> $DIR/map_collect_result_unit.rs:6:17 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + | + = note: `-D clippy::map-collect-result-unit` implied by `-D warnings` + +error: `.map().collect()` can be replaced with `.try_for_each()` + --> $DIR/map_collect_result_unit.rs:7:32 + | +LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + +error: aborting due to 2 previous errors + From bab338685f42e4b184c0f645c3a1a24a79890e17 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 24 Oct 2020 10:50:11 +0200 Subject: [PATCH 0825/1110] No lint in external macro for `toplevel_ref_arg` --- clippy_lints/src/misc.rs | 15 +++++++++---- tests/ui/auxiliary/macro_rules.rs | 14 ++++++++++++ tests/ui/toplevel_ref_arg.fixed | 21 ++++++++++++++++++ tests/ui/toplevel_ref_arg.rs | 21 ++++++++++++++++++ tests/ui/toplevel_ref_arg.stderr | 23 +++++++++++++++----- tests/ui/toplevel_ref_arg_non_rustfix.rs | 22 +++++++++++++++++++ tests/ui/toplevel_ref_arg_non_rustfix.stderr | 15 +++++++++++-- 7 files changed, 119 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 909e79f661a..308e92057b7 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -7,6 +7,7 @@ use rustc_hir::{ StmtKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::DesugaringKind; @@ -271,13 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { k: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, - _: Span, + span: Span, _: HirId, ) { if let FnKind::Closure(_) = k { // Does not apply to closures return; } + if in_external_macro(cx.tcx.sess, span) { + return; + } for arg in iter_input_pats(decl, body) { if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind { span_lint( @@ -293,13 +297,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if_chain! { + if !in_external_macro(cx.tcx.sess, stmt.span); if let StmtKind::Local(ref local) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; if let Some(ref init) = local.init; if !higher::is_from_for_desugar(local); then { if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { - let sugg_init = if init.span.from_expansion() { + // use the macro callsite when the init span (but not the whole local span) + // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` + let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, init, "..") } else { Sugg::hir(cx, init, "..") @@ -310,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", sugg_init.addr()) }; let tyopt = if let Some(ref ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_")) + format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) } else { String::new() }; @@ -326,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { "try", format!( "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, "_"), + name=snippet(cx, name.span, ".."), tyopt=tyopt, initref=initref, ), diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 0bbb9534928..93303865e17 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -56,3 +56,17 @@ macro_rules! option_env_unwrap_external { option_env!($env).expect($message) }; } + +#[macro_export] +macro_rules! ref_arg_binding { + () => { + let ref _y = 42; + }; +} + +#[macro_export] +macro_rules! ref_arg_function { + () => { + fn fun_example(ref _x: usize) {} + }; +} diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index 33605aca019..b129d95c560 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,7 +1,17 @@ // run-rustfix +// aux-build:macro_rules.rs #![warn(clippy::toplevel_ref_arg)] +#[macro_use] +extern crate macro_rules; + +macro_rules! gen_binding { + () => { + let _y = &42; + }; +} + fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -26,4 +36,15 @@ fn main() { // ok for ref _x in 0..10 {} + + // lint in macro + #[allow(unused)] + { + gen_binding!(); + } + + // do not lint in external macro + { + ref_arg_binding!(); + } } diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 59759f11893..73eb4ff7306 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,7 +1,17 @@ // run-rustfix +// aux-build:macro_rules.rs #![warn(clippy::toplevel_ref_arg)] +#[macro_use] +extern crate macro_rules; + +macro_rules! gen_binding { + () => { + let ref _y = 42; + }; +} + fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -26,4 +36,15 @@ fn main() { // ok for ref _x in 0..10 {} + + // lint in macro + #[allow(unused)] + { + gen_binding!(); + } + + // do not lint in external macro + { + ref_arg_binding!(); + } } diff --git a/tests/ui/toplevel_ref_arg.stderr b/tests/ui/toplevel_ref_arg.stderr index 19d69496709..15cb933fedc 100644 --- a/tests/ui/toplevel_ref_arg.stderr +++ b/tests/ui/toplevel_ref_arg.stderr @@ -1,5 +1,5 @@ error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:10:9 + --> $DIR/toplevel_ref_arg.rs:20:9 | LL | let ref _x = 1; | ----^^^^^^----- help: try: `let _x = &1;` @@ -7,28 +7,39 @@ LL | let ref _x = 1; = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:12:9 + --> $DIR/toplevel_ref_arg.rs:22:9 | LL | let ref _y: (&_, u8) = (&1, 2); | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:14:9 + --> $DIR/toplevel_ref_arg.rs:24:9 | LL | let ref _z = 1 + 2; | ----^^^^^^--------- help: try: `let _z = &(1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:16:9 + --> $DIR/toplevel_ref_arg.rs:26:9 | LL | let ref mut _z = 1 + 2; | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:21:9 + --> $DIR/toplevel_ref_arg.rs:31:9 | LL | let ref _x = vec![1, 2, 3]; | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];` -error: aborting due to 5 previous errors +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:11:13 + | +LL | let ref _y = 42; + | ----^^^^^^------ help: try: `let _y = &42;` +... +LL | gen_binding!(); + | --------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.rs b/tests/ui/toplevel_ref_arg_non_rustfix.rs index 42cac2ba4de..1a493fbce0e 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.rs +++ b/tests/ui/toplevel_ref_arg_non_rustfix.rs @@ -1,11 +1,33 @@ +// aux-build:macro_rules.rs + #![warn(clippy::toplevel_ref_arg)] #![allow(unused)] +#[macro_use] +extern crate macro_rules; + fn the_answer(ref mut x: u8) { *x = 42; } +macro_rules! gen_function { + () => { + fn fun_example(ref _x: usize) {} + }; +} + fn main() { let mut x = 0; the_answer(x); + + // lint in macro + #[allow(unused)] + { + gen_function!(); + } + + // do not lint in external macro + { + ref_arg_function!(); + } } diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 295e2f35608..6c36141a58c 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,10 +1,21 @@ error: `ref` directly on a function argument is ignored. Consider using a reference type instead. - --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15 + --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { | ^^^^^^^^^ | = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` -error: aborting due to previous error +error: `ref` directly on a function argument is ignored. Consider using a reference type instead. + --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24 + | +LL | fn fun_example(ref _x: usize) {} + | ^^^^^^ +... +LL | gen_function!(); + | ---------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors From 2911d9c7de4c0acf7bdd6a2d6983d1fccbeb6a69 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 18 Oct 2020 13:09:06 +0200 Subject: [PATCH 0826/1110] Use better placeholders for some methods lint messages --- clippy_lints/src/methods/mod.rs | 58 +++++++++---------- .../src/methods/option_map_unwrap_or.rs | 8 +-- tests/ui/filter_map_next.stderr | 4 +- tests/ui/filter_methods.stderr | 8 +-- tests/ui/find_map.stderr | 4 +- tests/ui/iter_skip_next.stderr | 8 +-- tests/ui/map_unwrap_or.stderr | 40 ++++++------- tests/ui/methods.stderr | 4 +- tests/ui/option_map_or_none.stderr | 4 +- tests/ui/skip_while_next.stderr | 8 +-- 10 files changed, 73 insertions(+), 73 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0824bacbc7..fc77cd52b8f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1748,7 +1748,7 @@ fn lint_or_fun_call<'tcx>( "try this", format!( "{}.unwrap_or_default()", - snippet_with_applicability(cx, self_expr.span, "_", &mut applicability) + snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) ), applicability, ); @@ -2155,7 +2155,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; - let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); + let snippet = snippet_with_macro_callsite(cx, arg.span, ".."); span_lint_and_sugg( cx, @@ -2191,9 +2191,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E "try this", format!( "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0].span, "..", &mut applicability), ref_str, - snippet_with_applicability(cx, target.span, "_", &mut applicability) + snippet_with_applicability(cx, target.span, "..", &mut applicability) ), applicability, ); @@ -2460,7 +2460,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: let mut applicability = Applicability::MachineApplicable; let expr_ty = cx.typeck_results().expr_ty(&get_args[0]); let get_args_str = if get_args.len() > 1 { - snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability) + snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability) } else { return; // not linting on a .get().unwrap() chain or variant }; @@ -2520,7 +2520,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: format!( "{}{}[{}]", borrow_str, - snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability), + snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability), get_args_str ), applicability, @@ -2536,7 +2536,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[ cx, ITER_SKIP_NEXT, expr.span.trim_start(caller.span).unwrap(), - "called `skip(x).next()` on an iterator", + "called `skip(..).next()` on an iterator", "use `nth` instead", hint, Applicability::MachineApplicable, @@ -2739,11 +2739,11 @@ fn lint_map_unwrap_or_else<'tcx>( // lint message let msg = if is_option { - "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(g, f)` instead" + "called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling \ + `map_or_else(, )` instead" } else { - "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(g, f)` instead" + "called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(, )` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2809,8 +2809,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map if is_option { let self_snippet = snippet(cx, map_or_args[0].span, ".."); let func_snippet = snippet(cx, map_or_args[2].span, ".."); - let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \ - `and_then(f)` instead"; + let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ + `and_then(..)` instead"; ( OPTION_MAP_OR_NONE, msg, @@ -2848,8 +2848,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.filter().next()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(p)` instead."; + let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { // add note if not multi-line @@ -2879,9 +2879,9 @@ fn lint_skip_while_next<'tcx>( cx, SKIP_WHILE_NEXT, expr.span, - "called `skip_while(p).next()` on an `Iterator`", + "called `skip_while(

).next()` on an `Iterator`", None, - "this is more succinctly expressed by calling `.find(!p)` instead", + "this is more succinctly expressed by calling `.find(!

).next()` on an `Iterator` --> $DIR/skip_while_next.rs:14:13 | LL | let _ = v.iter().skip_while(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::skip-while-next` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.find(!p)` instead + = help: this is more succinctly expressed by calling `.find(!

)` instead -error: called `skip_while(p).next()` on an `Iterator` +error: called `skip_while(

).next()` on an `Iterator` --> $DIR/skip_while_next.rs:17:13 | LL | let _ = v.iter().skip_while(|&x| { @@ -17,7 +17,7 @@ LL | | } LL | | ).next(); | |___________________________^ | - = help: this is more succinctly expressed by calling `.find(!p)` instead + = help: this is more succinctly expressed by calling `.find(!

)` instead error: aborting due to 2 previous errors From 3fec6f568daababf3520e2a818b4c1db79266b92 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 19 Oct 2020 18:47:02 +0200 Subject: [PATCH 0827/1110] Improve some suggestions for `filter_map_next`, `filter_next` and `map_unwrap_or` lints --- clippy_lints/src/methods/mod.rs | 30 ++++++++++++++++-------------- tests/ui/filter_map_next.stderr | 3 +-- tests/ui/map_unwrap_or.stderr | 16 ++++------------ tests/ui/methods.stderr | 3 +-- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc77cd52b8f..d3e26a09ad4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,8 +32,7 @@ use crate::utils::{ is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, - SpanlessEq, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -2753,16 +2752,15 @@ fn lint_map_unwrap_or_else<'tcx>( let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); if same_span && !multiline { - span_lint_and_note( + let var_snippet = snippet(cx, map_args[0].span, ".."); + span_lint_and_sugg( cx, MAP_UNWRAP_OR, expr.span, msg, - None, - &format!( - "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`", - map_snippet, unwrap_snippet, - ), + "try this", + format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + Applicability::MachineApplicable, ); return true; } else if same_span && multiline { @@ -2852,14 +2850,16 @@ fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, fil `.find(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, filter_args[0].span, ".."); // add note if not multi-line - span_lint_and_note( + span_lint_and_sugg( cx, FILTER_NEXT, expr.span, msg, - None, - &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet), + "try this", + format!("{}.find({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, ); } else { span_lint(cx, FILTER_NEXT, expr.span, msg); @@ -2908,13 +2908,15 @@ fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { - span_lint_and_note( + let iter_snippet = snippet(cx, filter_args[0].span, ".."); + span_lint_and_sugg( cx, FILTER_MAP_NEXT, expr.span, msg, - None, - &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet), + "try this", + format!("{}.find_map({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, ); } else { span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index 01281ebaf6a..bcedf11e536 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -2,10 +2,9 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly --> $DIR/filter_map_next.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` - = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:10:26 diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 6fa2800e9bd..1975abb3e9f 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -109,9 +109,7 @@ LL | let _ = opt.map(|x| x + 1) | _____________^ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or_else(|| 0); - | |_____________________________^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:55:13 @@ -137,25 +135,19 @@ error: called `map().unwrap_or_else()` on a `Result` value. This can be do --> $DIR/map_unwrap_or.rs:88:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:90:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:91:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: aborting due to 13 previous errors diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 74d8533d4da..2df1941aaaa 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -12,10 +12,9 @@ error: called `filter(..).next()` on an `Iterator`. This is more succinctly expr --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` | = note: `-D clippy::filter-next` implied by `-D warnings` - = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:129:13 From 2a3ae114858ff971b4cc51b4a43cb1475bd39516 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 23 Oct 2020 09:24:25 +0200 Subject: [PATCH 0828/1110] Move fixable `map_unwrap_or` cases to rustfixed test --- tests/ui/map_unwrap_or.rs | 41 -------------- tests/ui/map_unwrap_or.stderr | 61 ++++----------------- tests/ui/map_unwrap_or_else_fixable.fixed | 59 ++++++++++++++++++++ tests/ui/map_unwrap_or_else_fixable.rs | 63 ++++++++++++++++++++++ tests/ui/map_unwrap_or_else_fixable.stderr | 40 ++++++++++++++ 5 files changed, 172 insertions(+), 92 deletions(-) create mode 100644 tests/ui/map_unwrap_or_else_fixable.fixed create mode 100644 tests/ui/map_unwrap_or_else_fixable.rs create mode 100644 tests/ui/map_unwrap_or_else_fixable.stderr diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 585944032e7..4e977051ab7 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,4 +1,3 @@ -// FIXME: Add "run-rustfix" once it's supported for multipart suggestions // aux-build:option_helpers.rs #![warn(clippy::map_unwrap_or)] @@ -13,10 +12,6 @@ fn option_methods() { let opt = Some(1); // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); // Multi-line cases. let _ = opt.map(|x| { x + 1 @@ -47,10 +42,6 @@ fn option_methods() { let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); // Multi-line cases. let _ = opt.map(|x| { x + 1 @@ -60,40 +51,8 @@ fn option_methods() { .unwrap_or_else(|| 0 ); - // Macro case. - // Should not lint. - let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - - // Issue #4144 - { - let mut frequencies = HashMap::new(); - let word = "foo"; - - frequencies - .get_mut(word) - .map(|count| { - *count += 1; - }) - .unwrap_or_else(|| { - frequencies.insert(word.to_owned(), 1); - }); - } -} - -fn result_methods() { - let res: Result = Ok(1); - - // Check for `result.map(_).unwrap_or_else(_)` use. - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint } fn main() { option_methods(); - result_methods(); } diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 1975abb3e9f..3fd4bdfd2b9 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,20 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:17:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or(0); - | |_____________________^ - | - = note: `-D clippy::map-unwrap-or` implied by `-D warnings` -help: use `map_or(, )` instead - | -LL | let _ = opt.map_or(0, |x| x + 1); - | ^^^^^^ ^^ -- - -error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:21:13 + --> $DIR/map_unwrap_or.rs:16:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -23,6 +8,7 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { @@ -32,7 +18,7 @@ LL | ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:25:13 + --> $DIR/map_unwrap_or.rs:20:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -49,7 +35,7 @@ LL | }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:30:13 + --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +46,7 @@ LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:32:13 + --> $DIR/map_unwrap_or.rs:27:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -78,7 +64,7 @@ LL | ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:36:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt | _____________^ @@ -92,7 +78,7 @@ LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:47:13 + --> $DIR/map_unwrap_or.rs:42:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,16 +89,7 @@ LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:51:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:55:13 + --> $DIR/map_unwrap_or.rs:46:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -122,7 +99,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:59:13 + --> $DIR/map_unwrap_or.rs:50:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -131,23 +108,5 @@ LL | | 0 LL | | ); | |_________^ -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:88:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:90:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:91:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` - -error: aborting due to 13 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or_else_fixable.fixed b/tests/ui/map_unwrap_or_else_fixable.fixed new file mode 100644 index 00000000000..cb2492a3be0 --- /dev/null +++ b/tests/ui/map_unwrap_or_else_fixable.fixed @@ -0,0 +1,59 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map_or_else(|| 0, |x| x + 1); + + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map_or_else(|| 0, |x| x + 1); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map_or_else(|_e| 0, |x| x + 1); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map_or_else(|_e| 0, |x| x + 1); + let _ = res.map_or_else(|_e| 0, |x| x + 1); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or_else_fixable.rs b/tests/ui/map_unwrap_or_else_fixable.rs new file mode 100644 index 00000000000..ed762dacd87 --- /dev/null +++ b/tests/ui/map_unwrap_or_else_fixable.rs @@ -0,0 +1,63 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or_else_fixable.stderr b/tests/ui/map_unwrap_or_else_fixable.stderr new file mode 100644 index 00000000000..2cb76d70684 --- /dev/null +++ b/tests/ui/map_unwrap_or_else_fixable.stderr @@ -0,0 +1,40 @@ +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` + +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:27:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:52:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:54:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:55:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 5 previous errors + From e2d86b5b809584da55952f4150016fdbaf74e7f4 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 23 Oct 2020 09:49:47 +0200 Subject: [PATCH 0829/1110] Move fixable `filter_next` and `filter_map_next` cases to rustfixed tests --- tests/ui/filter_map_next.rs | 3 --- tests/ui/filter_map_next.stderr | 14 ++++-------- tests/ui/filter_map_next_fixable.fixed | 10 +++++++++ tests/ui/filter_map_next_fixable.rs | 10 +++++++++ tests/ui/filter_map_next_fixable.stderr | 10 +++++++++ tests/ui/methods.rs | 5 +---- tests/ui/methods.stderr | 30 ++++++++++--------------- tests/ui/methods_fixable.fixed | 11 +++++++++ tests/ui/methods_fixable.rs | 11 +++++++++ tests/ui/methods_fixable.stderr | 10 +++++++++ 10 files changed, 79 insertions(+), 35 deletions(-) create mode 100644 tests/ui/filter_map_next_fixable.fixed create mode 100644 tests/ui/filter_map_next_fixable.rs create mode 100644 tests/ui/filter_map_next_fixable.stderr create mode 100644 tests/ui/methods_fixable.fixed create mode 100644 tests/ui/methods_fixable.rs create mode 100644 tests/ui/methods_fixable.stderr diff --git a/tests/ui/filter_map_next.rs b/tests/ui/filter_map_next.rs index f5d051be198..dbeb2354309 100644 --- a/tests/ui/filter_map_next.rs +++ b/tests/ui/filter_map_next.rs @@ -3,9 +3,6 @@ fn main() { let a = ["1", "lol", "3", "NaN", "5"]; - let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - assert_eq!(element, Some(1)); - #[rustfmt::skip] let _: Option = vec![1, 2, 3, 4, 5, 6] .into_iter() diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index bcedf11e536..45427684d96 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -1,13 +1,5 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. - --> $DIR/filter_map_next.rs:6:32 - | -LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` - | - = note: `-D clippy::filter-map-next` implied by `-D warnings` - -error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. - --> $DIR/filter_map_next.rs:10:26 + --> $DIR/filter_map_next.rs:7:26 | LL | let _: Option = vec![1, 2, 3, 4, 5, 6] | __________________________^ @@ -18,6 +10,8 @@ LL | | if x == 2 { LL | | }) LL | | .next(); | |_______________^ + | + = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed new file mode 100644 index 00000000000..c3992d7e92c --- /dev/null +++ b/tests/ui/filter_map_next_fixable.fixed @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] + +fn main() { + let a = ["1", "lol", "3", "NaN", "5"]; + + let element: Option = a.iter().find_map(|s| s.parse().ok()); + assert_eq!(element, Some(1)); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs new file mode 100644 index 00000000000..447219a9683 --- /dev/null +++ b/tests/ui/filter_map_next_fixable.rs @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] + +fn main() { + let a = ["1", "lol", "3", "NaN", "5"]; + + let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + assert_eq!(element, Some(1)); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr new file mode 100644 index 00000000000..6c2530e0379 --- /dev/null +++ b/tests/ui/filter_map_next_fixable.stderr @@ -0,0 +1,10 @@ +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. + --> $DIR/filter_map_next_fixable.rs:8:32 + | +LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + | + = note: `-D clippy::filter-map-next` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 80dd2f744b3..d93e5b114ec 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -122,16 +122,13 @@ impl Mul for T { fn filter_next() { let v = vec![3, 2, 1, 0, -1, -2, -3]; - // Single-line case. - let _ = v.iter().filter(|&x| *x < 0).next(); - // Multi-line case. let _ = v.iter().filter(|&x| { *x < 0 } ).next(); - // Check that hat we don't lint if the caller is not an `Iterator`. + // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next(); } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 2df1941aaaa..8a281c2dbd2 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -11,23 +11,17 @@ LL | | } error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:126:13 | -LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` - | - = note: `-D clippy::filter-next` implied by `-D warnings` - -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. - --> $DIR/methods.rs:129:13 - | LL | let _ = v.iter().filter(|&x| { | _____________^ LL | | *x < 0 LL | | } LL | | ).next(); | |___________________________^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:146:22 + --> $DIR/methods.rs:143:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -35,25 +29,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:147:20 + --> $DIR/methods.rs:144:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:148:20 + --> $DIR/methods.rs:145:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:149:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:152:13 + --> $DIR/methods.rs:149:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -63,13 +57,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:158:22 + --> $DIR/methods.rs:155:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:161:13 + --> $DIR/methods.rs:158:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -79,13 +73,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:167:22 + --> $DIR/methods.rs:164:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:170:13 + --> $DIR/methods.rs:167:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -94,5 +88,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed new file mode 100644 index 00000000000..ee7c1b0da6d --- /dev/null +++ b/tests/ui/methods_fixable.fixed @@ -0,0 +1,11 @@ +// run-rustfix + +#![warn(clippy::filter_next)] + +/// Checks implementation of `FILTER_NEXT` lint. +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().find(|&x| *x < 0); +} diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs new file mode 100644 index 00000000000..6d0f1b7bd51 --- /dev/null +++ b/tests/ui/methods_fixable.rs @@ -0,0 +1,11 @@ +// run-rustfix + +#![warn(clippy::filter_next)] + +/// Checks implementation of `FILTER_NEXT` lint. +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); +} diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr new file mode 100644 index 00000000000..70e7c3dea54 --- /dev/null +++ b/tests/ui/methods_fixable.stderr @@ -0,0 +1,10 @@ +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. + --> $DIR/methods_fixable.rs:10:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` + | + = note: `-D clippy::filter-next` implied by `-D warnings` + +error: aborting due to previous error + From c0dd1f9f7614d86c15fcccd4e0faabaa52c7c339 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 26 Oct 2020 11:02:01 +0100 Subject: [PATCH 0830/1110] Fix tests for `map_unwrap_or*` --- tests/ui/map_unwrap_or.rs | 23 ++++++++ tests/ui/map_unwrap_or.stderr | 52 +++++++++++++++---- tests/ui/map_unwrap_or_else_fixable.stderr | 40 -------------- ...able.fixed => map_unwrap_or_fixable.fixed} | 9 +--- ...se_fixable.rs => map_unwrap_or_fixable.rs} | 15 ++---- tests/ui/map_unwrap_or_fixable.stderr | 22 ++++++++ 6 files changed, 95 insertions(+), 66 deletions(-) delete mode 100644 tests/ui/map_unwrap_or_else_fixable.stderr rename tests/ui/{map_unwrap_or_else_fixable.fixed => map_unwrap_or_fixable.fixed} (75%) rename tests/ui/{map_unwrap_or_else_fixable.rs => map_unwrap_or_fixable.rs} (69%) create mode 100644 tests/ui/map_unwrap_or_fixable.stderr diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 4e977051ab7..87e16f5d09b 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -12,6 +12,10 @@ fn option_methods() { let opt = Some(1); // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); // Multi-line cases. let _ = opt.map(|x| { x + 1 @@ -53,6 +57,25 @@ fn option_methods() { ); } +#[rustfmt::skip] +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // multi line cases + let _ = res.map(|x| { + x + 1 + } + ).unwrap_or_else(|_e| 0); + let _ = res.map(|x| x + 1) + .unwrap_or_else(|_e| { + 0 + }); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + fn main() { option_methods(); + result_methods(); } diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 3fd4bdfd2b9..96b9d6cc3c1 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,6 +1,21 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:16:13 | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or(0); + | |_____________________^ + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` +help: use `map_or(, )` instead + | +LL | let _ = opt.map_or(0, |x| x + 1); + | ^^^^^^ ^^ -- + +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead + --> $DIR/map_unwrap_or.rs:20:13 + | LL | let _ = opt.map(|x| { | _____________^ LL | | x + 1 @@ -8,7 +23,6 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | - = note: `-D clippy::map-unwrap-or` implied by `-D warnings` help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { @@ -18,7 +32,7 @@ LL | ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:24:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -35,7 +49,7 @@ LL | }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:25:13 + --> $DIR/map_unwrap_or.rs:29:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +60,7 @@ LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:27:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -64,7 +78,7 @@ LL | ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:35:13 | LL | let _ = opt | _____________^ @@ -78,7 +92,7 @@ LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:42:13 + --> $DIR/map_unwrap_or.rs:46:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +103,7 @@ LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:50:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -99,7 +113,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:54:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -108,5 +122,25 @@ LL | | 0 LL | | ); | |_________^ -error: aborting due to 8 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:66:13 + | +LL | let _ = res.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or_else(|_e| 0); + | |____________________________^ + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:70:13 + | +LL | let _ = res.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|_e| { +LL | | 0 +LL | | }); + | |__________^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/map_unwrap_or_else_fixable.stderr b/tests/ui/map_unwrap_or_else_fixable.stderr deleted file mode 100644 index 2cb76d70684..00000000000 --- a/tests/ui/map_unwrap_or_else_fixable.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:17:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` - | - = note: `-D clippy::map-unwrap-or` implied by `-D warnings` - -error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:27:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:52:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:54:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:55:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/map_unwrap_or_else_fixable.fixed b/tests/ui/map_unwrap_or_fixable.fixed similarity index 75% rename from tests/ui/map_unwrap_or_else_fixable.fixed rename to tests/ui/map_unwrap_or_fixable.fixed index cb2492a3be0..bd5b4f7165a 100644 --- a/tests/ui/map_unwrap_or_else_fixable.fixed +++ b/tests/ui/map_unwrap_or_fixable.fixed @@ -20,10 +20,6 @@ fn option_methods() { // Should not lint. let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map_or_else(|| 0, |x| x + 1); - // Issue #4144 { let mut frequencies = HashMap::new(); @@ -40,15 +36,14 @@ fn option_methods() { } } +#[rustfmt::skip] fn result_methods() { let res: Result = Ok(1); // Check for `result.map(_).unwrap_or_else(_)` use. // single line case - let _ = res.map_or_else(|_e| 0, |x| x + 1); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map_or_else(|_e| 0, |x| x + 1); let _ = res.map_or_else(|_e| 0, |x| x + 1); + // macro case let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint } diff --git a/tests/ui/map_unwrap_or_else_fixable.rs b/tests/ui/map_unwrap_or_fixable.rs similarity index 69% rename from tests/ui/map_unwrap_or_else_fixable.rs rename to tests/ui/map_unwrap_or_fixable.rs index ed762dacd87..0b892caf20e 100644 --- a/tests/ui/map_unwrap_or_else_fixable.rs +++ b/tests/ui/map_unwrap_or_fixable.rs @@ -22,12 +22,6 @@ fn option_methods() { // Should not lint. let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); - // Issue #4144 { let mut frequencies = HashMap::new(); @@ -44,15 +38,16 @@ fn option_methods() { } } +#[rustfmt::skip] fn result_methods() { let res: Result = Ok(1); // Check for `result.map(_).unwrap_or_else(_)` use. // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + let _ = res.map(|x| x + 1) + // should lint even though this call is on a separate line + .unwrap_or_else(|_e| 0); + // macro case let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint } diff --git a/tests/ui/map_unwrap_or_fixable.stderr b/tests/ui/map_unwrap_or_fixable.stderr new file mode 100644 index 00000000000..1837bc2ca3b --- /dev/null +++ b/tests/ui/map_unwrap_or_fixable.stderr @@ -0,0 +1,22 @@ +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or_fixable.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_fixable.rs:47:13 + | +LL | let _ = res.map(|x| x + 1) + | _____________^ +LL | | // should lint even though this call is on a separate line +LL | | .unwrap_or_else(|_e| 0); + | |_______________________________^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 2 previous errors + From 213dbf7aacb01cc4d05d47f010833aa6e9c2a7d0 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 12 Oct 2020 23:58:59 +0200 Subject: [PATCH 0831/1110] clippy_lint: Add 'ref_option_ref' --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/ref_option_ref.rs | 65 ++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/ref_option_ref.rs | 5 +++ tests/ui/ref_option_ref.stderr | 10 +++++ 6 files changed, 93 insertions(+) create mode 100644 clippy_lints/src/ref_option_ref.rs create mode 100644 tests/ui/ref_option_ref.rs create mode 100644 tests/ui/ref_option_ref.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..db834fe108b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1917,6 +1917,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..1ab36231758 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; +mod ref_option_ref; mod reference; mod regex; mod repeat_once; @@ -803,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + &ref_option_ref::REF_OPTION_REF, &reference::DEREF_ADDROF, &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, @@ -1024,6 +1026,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &sess.target, ); store.register_late_pass(move || box pass_by_ref_or_value); + store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); @@ -1493,6 +1496,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), @@ -1648,6 +1652,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs new file mode 100644 index 00000000000..fbee3263556 --- /dev/null +++ b/clippy_lints/src/ref_option_ref.rs @@ -0,0 +1,65 @@ +use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; +use rustc_hir::{GenericArg, Local, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use if_chain::if_chain; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `&Option<&T>`. + /// + /// **Why is this bad?** Since `&` is Copy, it's useless to have a + /// reference on `Option<&T>`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // example code where clippy issues a warning + /// let x: &Option<&u32> = &Some(&0u32); + /// ``` + /// Use instead: + /// ```rust,ignore + /// // example code which does not raise clippy warning + /// let x: Option<&u32> = Some(&0u32); + /// ``` + pub REF_OPTION_REF, + style, + "use `Option<&T>` instead of `&Option<&T>`" +} + +declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); + +impl<'tcx> LateLintPass<'tcx> for RefOptionRef { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + if_chain! { + if let Some(ref ty) = local.ty; + if let TyKind::Rptr(_, ref mut_ty) = ty.kind; + if mut_ty.mutbl == Mutability::Not; + if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; + if let Some(def_id) = cx.typeck_results().qpath_res(qpath, local.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::OPTION); + if let Some(ref params) = last_path_segment(qpath).args ; + if !params.parenthesized; + if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(inner_ty) => Some(inner_ty), + _ => None, + }); + if let TyKind::Rptr(_, _) = inner_ty.kind; + + then { + span_lint_and_sugg( + cx, + REF_OPTION_REF, + ty.span, + "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", + "try", + format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..b3bd1923d4e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2013,6 +2013,13 @@ vec![ deprecation: None, module: "reference", }, + Lint { + name: "ref_option_ref", + group: "style", + desc: "use `Option<&T>` instead of `&Option<&T>`", + deprecation: None, + module: "ref_option_ref", + }, Lint { name: "repeat_once", group: "complexity", diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs new file mode 100644 index 00000000000..7f05990c0a0 --- /dev/null +++ b/tests/ui/ref_option_ref.rs @@ -0,0 +1,5 @@ +#![warn(clippy::ref_option_ref)] + +fn main() { + let x: &Option<&u32> = &None; +} diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr new file mode 100644 index 00000000000..90bcaef7570 --- /dev/null +++ b/tests/ui/ref_option_ref.stderr @@ -0,0 +1,10 @@ +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:4:12 + | +LL | let x: &Option<&u32> = &None; + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + | + = note: `-D clippy::ref-option-ref` implied by `-D warnings` + +error: aborting due to previous error + From d1baa25f046c8067ee1c9fa9e134de3ca0f64834 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 14:16:33 +0200 Subject: [PATCH 0832/1110] clippy_lint: Add 'ref_option_ref' refactor code --- clippy_lints/src/ref_option_ref.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index fbee3263556..40ac2b3f4f1 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,5 +1,5 @@ use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; -use rustc_hir::{GenericArg, Local, Mutability, TyKind}; +use rustc_hir::{GenericArg, Local, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,12 +34,20 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + + if let Some(ref ty) = local.ty { + self.check_ref_option_ref(cx, ty); + } + } +} + +impl RefOptionRef { + fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if_chain! { - if let Some(ref ty) = local.ty; if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; - if let Some(def_id) = cx.typeck_results().qpath_res(qpath, local.hir_id).opt_def_id(); + if let Some(def_id) = cx.typeck_results().qpath_res(qpath, ty.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::OPTION); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; @@ -57,9 +65,9 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", "try", format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), - Applicability::MachineApplicable, + Applicability::Unspecified, ); } } } -} +} \ No newline at end of file From 469b2fc7811c14fa645dceadfb976fb4f4ef248a Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:13:54 +0200 Subject: [PATCH 0833/1110] clippy_lint: Add 'ref_option_ref' move to check_ty and add type alias test --- clippy_lints/src/ref_option_ref.rs | 16 ++++++++-------- tests/ui/ref_option_ref.rs | 4 ++++ tests/ui/ref_option_ref.stderr | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 40ac2b3f4f1..4b5bc93d8a1 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -33,11 +33,8 @@ declare_clippy_lint! { declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - - if let Some(ref ty) = local.ty { - self.check_ref_option_ref(cx, ty); - } + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + self.check_ref_option_ref(cx, ty); } } @@ -46,8 +43,11 @@ impl RefOptionRef { if_chain! { if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; - if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; - if let Some(def_id) = cx.typeck_results().qpath_res(qpath, ty.hir_id).opt_def_id(); + if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; + let last = last_path_segment(qpath); + if let Some(res) = last.res; + if let Some(def_id) = res.opt_def_id(); + if match_def_path(cx, def_id, &paths::OPTION); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; @@ -70,4 +70,4 @@ impl RefOptionRef { } } } -} \ No newline at end of file +} diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 7f05990c0a0..24db42efb80 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,5 +1,9 @@ +#![allow(unused)] #![warn(clippy::ref_option_ref)] +type OptRefU32<'a> = &'a Option<&'a u32>; +type OptRef<'a, T> = &'a Option<&'a T>; + fn main() { let x: &Option<&u32> = &None; } diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 90bcaef7570..26f4065d311 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,10 +1,22 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:4:12 + --> $DIR/ref_option_ref.rs:4:22 | -LL | let x: &Option<&u32> = &None; - | ^^^^^^^^^^^^^ help: try: `Option<&u32>` +LL | type OptRefU32<'a> = &'a Option<&'a u32>; + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` | = note: `-D clippy::ref-option-ref` implied by `-D warnings` -error: aborting due to previous error +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:5:22 + | +LL | type OptRef<'a, T> = &'a Option<&'a T>; + | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:8:12 + | +LL | let x: &Option<&u32> = &None; + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: aborting due to 3 previous errors From c1f3bab6b19ee27bce5b8c4df0d8413da7728473 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:46:47 +0200 Subject: [PATCH 0834/1110] clippy_lint: Add 'ref_option_ref' remove unused import --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 4b5bc93d8a1..3b9465ec13d 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,5 +1,5 @@ use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; -use rustc_hir::{GenericArg, Local, Mutability, Ty, TyKind}; +use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; From bdd76a9d1c4140624d758e5b8727869db3f9207c Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:47:46 +0200 Subject: [PATCH 0835/1110] clippy_lint: Allow 'ref_option_ref' for 'option_if_let_else' --- tests/ui/option_if_let_else.fixed | 1 + tests/ui/option_if_let_else.rs | 1 + tests/ui/option_if_let_else.stderr | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index a7fb00a2705..47e7460fa7a 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] +#![allow(clippy::ref_option_ref)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 895fd86321f..e2f8dec3b93 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] +#![allow(clippy::ref_option_ref)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index b69fe767682..7aab068800a 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:6:5 + --> $DIR/option_if_let_else.rs:7:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:16:12 + --> $DIR/option_if_let_else.rs:17:12 | LL | } else if let Some(x) = string { | ____________^ @@ -22,19 +22,19 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:26:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:27:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -54,13 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:33:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:34:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -80,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -100,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:48:5 + --> $DIR/option_if_let_else.rs:49:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -119,7 +119,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:62:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:71:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,7 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:99:13 + --> $DIR/option_if_let_else.rs:100:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` From b41b38cb7f16e1a622738aff591268083dca2e39 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 23:01:57 +0200 Subject: [PATCH 0836/1110] clippy_lint: Refactor 'ref_option_ref' --- clippy_lints/src/ref_option_ref.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 3b9465ec13d..d4b09848ab6 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -34,12 +34,6 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { - self.check_ref_option_ref(cx, ty); - } -} - -impl RefOptionRef { - fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if_chain! { if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; From 2270ff4d160fc65de4401a0984d2957c6d7a8186 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Thu, 15 Oct 2020 21:56:56 +0200 Subject: [PATCH 0837/1110] clippy_lint: Add test cases --- tests/ui/ref_option_ref.rs | 37 ++++++++++++++++++-- tests/ui/ref_option_ref.stderr | 64 +++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 24db42efb80..3c4def272f7 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,8 +1,41 @@ #![allow(unused)] #![warn(clippy::ref_option_ref)] -type OptRefU32<'a> = &'a Option<&'a u32>; -type OptRef<'a, T> = &'a Option<&'a T>; +static THRESHOLD: i32 = 10; +static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); +const CONST_THRESHOLD: &i32 = &10; +const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); + +type RefOptRefU32<'a> = &'a Option<&'a u32>; +type RefOptRef<'a, T> = &'a Option<&'a T>; + +fn foo(data: &Option<&u32>) {} + +fn bar(data: &u32) -> &Option<&u32> { + &None +} + +struct StructRef<'a> { + data: &'a Option<&'a u32>, +} + +struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); + +enum EnumRef<'a> { + Variant1(u32), + Variant2(&'a Option<&'a u32>), +} + +trait RefOptTrait { + type A; + fn foo(&self, _: Self::A); +} + +impl RefOptTrait for u32 { + type A = &'static Option<&'static Self>; + + fn foo(&self, _: Self::A) {} +} fn main() { let x: &Option<&u32> = &None; diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 26f4065d311..16c1de19bb6 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,22 +1,70 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:4:22 + --> $DIR/ref_option_ref.rs:5:23 | -LL | type OptRefU32<'a> = &'a Option<&'a u32>; - | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` +LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); + | ^^^^^^^^^^^^^ help: try: `Option<&i32>` | = note: `-D clippy::ref-option-ref` implied by `-D warnings` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:5:22 + --> $DIR/ref_option_ref.rs:7:18 | -LL | type OptRef<'a, T> = &'a Option<&'a T>; - | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` +LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); + | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:8:12 + --> $DIR/ref_option_ref.rs:9:25 + | +LL | type RefOptRefU32<'a> = &'a Option<&'a u32>; + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:10:25 + | +LL | type RefOptRef<'a, T> = &'a Option<&'a T>; + | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:12:14 + | +LL | fn foo(data: &Option<&u32>) {} + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:14:23 + | +LL | fn bar(data: &u32) -> &Option<&u32> { + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:19:11 + | +LL | data: &'a Option<&'a u32>, + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:22:32 + | +LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:26:14 + | +LL | Variant2(&'a Option<&'a u32>), + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:35:14 + | +LL | type A = &'static Option<&'static Self>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:41:12 | LL | let x: &Option<&u32> = &None; | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors From 8e26004a5ff2d16a7ad37723f5d284703cff2b94 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 19 Oct 2020 21:37:36 +0200 Subject: [PATCH 0838/1110] Update clippy_lints/src/ref_option_ref.rs doctest Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index d4b09848ab6..902cd06f527 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -17,12 +17,10 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust,ignore - /// // example code where clippy issues a warning /// let x: &Option<&u32> = &Some(&0u32); /// ``` /// Use instead: /// ```rust,ignore - /// // example code which does not raise clippy warning /// let x: Option<&u32> = Some(&0u32); /// ``` pub REF_OPTION_REF, From 1566db704d113ea6ed90f2b3927306bd84946dc2 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 19 Oct 2020 21:38:36 +0200 Subject: [PATCH 0839/1110] Update clippy_lints/src/ref_option_ref.rs Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 902cd06f527..4ab2d8025e0 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -40,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(res) = last.res; if let Some(def_id) = res.opt_def_id(); - if match_def_path(cx, def_id, &paths::OPTION); + if cx.tcx.is_diagnostic_item(sym!(option_type), def_id); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { From 7fd74c6bf676e535592f5baadd412bf7c98e078c Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 19 Oct 2020 22:01:37 +0200 Subject: [PATCH 0840/1110] clippy_lint: Add Known Problems message --- clippy_lints/src/ref_option_ref.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 4ab2d8025e0..715938df138 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,4 +1,4 @@ -use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; +use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -12,7 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** Since `&` is Copy, it's useless to have a /// reference on `Option<&T>`. /// - /// **Known problems:** None. + /// **Known problems:** It may be irrevelent to use this lint on + /// public API code as it will make a breaking change to apply it. /// /// **Example:** /// From db40a07665f74e58fbf2f161dcd618f6945ec0e3 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 20 Oct 2020 01:23:39 +0200 Subject: [PATCH 0841/1110] Update clippy_lints/src/ref_option_ref.rs Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 715938df138..a83f11bf670 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// let x: Option<&u32> = Some(&0u32); /// ``` pub REF_OPTION_REF, - style, + pedantic, "use `Option<&T>` instead of `&Option<&T>`" } From ffddb669e02e5f38620d839756bb2d02fff6c5e8 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 20 Oct 2020 07:11:25 +0200 Subject: [PATCH 0842/1110] clippy_lint: run after changing category to pendantic --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1ab36231758..cdfad908fc2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1255,6 +1255,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), @@ -1496,7 +1497,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), @@ -1652,7 +1652,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b3bd1923d4e..8398c132b33 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2015,7 +2015,7 @@ vec![ }, Lint { name: "ref_option_ref", - group: "style", + group: "pedantic", desc: "use `Option<&T>` instead of `&Option<&T>`", deprecation: None, module: "ref_option_ref", From 8337c467e9968b91277f0c0ae113e2d4917ac55e Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 26 Oct 2020 22:33:48 +0100 Subject: [PATCH 0843/1110] Change Applicability to MaybeIncorrect Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index a83f11bf670..e8c60842e76 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", "try", format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), - Applicability::Unspecified, + Applicability::MaybeIncorrect, ); } } From 6212950ceb9add443db7450f2e4790fbb2fbddd7 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 26 Oct 2020 22:50:13 +0100 Subject: [PATCH 0844/1110] Explain why 'run-rustfix' is not defined --- tests/ui/ref_option_ref.rs | 5 +++++ tests/ui/ref_option_ref.stderr | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 3c4def272f7..b2c275d68af 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,6 +1,11 @@ #![allow(unused)] #![warn(clippy::ref_option_ref)] +// This lint is not tagged as run-rustfix because automatically +// changing the type of a variable would also means changing +// all usages of this variable to match and This is not handled +// by this lint. + static THRESHOLD: i32 = 10; static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); const CONST_THRESHOLD: &i32 = &10; diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 16c1de19bb6..10f19661268 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,5 +1,5 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:5:23 + --> $DIR/ref_option_ref.rs:10:23 | LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` @@ -7,61 +7,61 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); = note: `-D clippy::ref-option-ref` implied by `-D warnings` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:7:18 + --> $DIR/ref_option_ref.rs:12:18 | LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:9:25 + --> $DIR/ref_option_ref.rs:14:25 | LL | type RefOptRefU32<'a> = &'a Option<&'a u32>; | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:10:25 + --> $DIR/ref_option_ref.rs:15:25 | LL | type RefOptRef<'a, T> = &'a Option<&'a T>; | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:12:14 + --> $DIR/ref_option_ref.rs:17:14 | LL | fn foo(data: &Option<&u32>) {} | ^^^^^^^^^^^^^ help: try: `Option<&u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:14:23 + --> $DIR/ref_option_ref.rs:19:23 | LL | fn bar(data: &u32) -> &Option<&u32> { | ^^^^^^^^^^^^^ help: try: `Option<&u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:19:11 + --> $DIR/ref_option_ref.rs:24:11 | LL | data: &'a Option<&'a u32>, | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:22:32 + --> $DIR/ref_option_ref.rs:27:32 | LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:26:14 + --> $DIR/ref_option_ref.rs:31:14 | LL | Variant2(&'a Option<&'a u32>), | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:35:14 + --> $DIR/ref_option_ref.rs:40:14 | LL | type A = &'static Option<&'static Self>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:41:12 + --> $DIR/ref_option_ref.rs:46:12 | LL | let x: &Option<&u32> = &None; | ^^^^^^^^^^^^^ help: try: `Option<&u32>` From e568a328f9eb528653351643b1482ccecada1480 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 21 Oct 2020 09:42:00 +1300 Subject: [PATCH 0845/1110] fix the error-causing suggestion of 'borrowed_box' fix the error-causing suggestion of 'borrowed_box', which missed parentheses and was ambiguous. --- clippy_lints/src/types.rs | 44 +++++++++++++++++++++++++++------ tests/ui/borrow_box.rs | 16 ++++++++++++ tests/ui/borrow_box.stderr | 50 +++++++++++++++++++++++++++++++++++--- 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6a33aaaaab2..45f3bc3ea85 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, - TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, + BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, + ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, + StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -678,17 +678,30 @@ impl Types { // details. return; } + + // When trait objects or opaque types have lifetime or auto-trait bounds, + // we need to add parentheses to avoid a syntax error due to its ambiguity. + // Originally reported as the issue #3128. + let inner_snippet = snippet(cx, inner.span, ".."); + let suggestion = match &inner.kind { + TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => { + format!("&{}({})", ltopt, &inner_snippet) + }, + TyKind::Path(qpath) + if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) + .map_or(false, |bounds| bounds.len() > 1) => + { + format!("&{}({})", ltopt, &inner_snippet) + }, + _ => format!("&{}{}", ltopt, &inner_snippet), + }; span_lint_and_sugg( cx, BORROWED_BOX, hir_ty.span, "you seem to be trying to use `&Box`. Consider using just `&T`", "try", - format!( - "&{}{}", - ltopt, - &snippet(cx, inner.span, "..") - ), + suggestion, // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item // because the trait impls of it will break otherwise; // and there may be other cases that result in invalid code. @@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { false } +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { + if_chain! { + if let Some(did) = qpath_res(cx, qpath, id).opt_def_id(); + if let Some(node) = cx.tcx.hir().get_if_local(did); + if let Node::GenericParam(generic_param) = node; + if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; + if synthetic == Some(SyntheticTyParamKind::ImplTrait); + then { + Some(generic_param.bounds) + } else { + None + } + } +} + declare_clippy_lint! { /// **What it does:** Checks for binding a unit value. /// diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 1901de46ca8..b606f773cfb 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -3,6 +3,8 @@ #![allow(unused_variables)] #![allow(dead_code)] +use std::fmt::Display; + pub fn test1(foo: &mut Box) { // Although this function could be changed to "&mut bool", // avoiding the Box, mutable references to boxes are not @@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) { *boxed_slice = data.into_boxed_slice(); } +// The suggestion should include proper parentheses to avoid a syntax error. +pub fn test14(_display: &Box) {} +pub fn test15(_display: &Box) {} +pub fn test16<'a>(_display: &'a Box) {} + +pub fn test17(_display: &Box) {} +pub fn test18(_display: &Box) {} +pub fn test19<'a>(_display: &'a Box) {} + +// This exists only to check what happens when parentheses are already present. +// Even though the current implementation doesn't put extra parentheses, +// it's fine that unnecessary parentheses appear in the future for some reason. +pub fn test20(_display: &Box<(dyn Display + Send)>) {} + fn main() { test1(&mut Box::new(false)); test2(); diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index b5db691f89f..3eac32815be 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:19:14 + --> $DIR/borrow_box.rs:21:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:23:10 + --> $DIR/borrow_box.rs:25:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:27:17 + --> $DIR/borrow_box.rs:29:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` -error: aborting due to 3 previous errors +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:95:25 + | +LL | pub fn test14(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:96:25 + | +LL | pub fn test15(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:97:29 + | +LL | pub fn test16<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:99:25 + | +LL | pub fn test17(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:100:25 + | +LL | pub fn test18(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:101:29 + | +LL | pub fn test19<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:106:25 + | +LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: aborting due to 10 previous errors From 111b9023dad65721300a39c3cf337f6bfb96d5d3 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 27 Oct 2020 01:47:40 +0100 Subject: [PATCH 0846/1110] add manual_ok_or lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/manual_ok_or.rs | 97 ++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/manual_ok_or.fixed | 40 +++++++++++++ tests/ui/manual_ok_or.rs | 44 +++++++++++++++ tests/ui/manual_ok_or.stderr | 41 ++++++++++++++ 7 files changed, 234 insertions(+) create mode 100644 clippy_lints/src/manual_ok_or.rs create mode 100644 tests/ui/manual_ok_or.fixed create mode 100644 tests/ui/manual_ok_or.rs create mode 100644 tests/ui/manual_ok_or.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..cd884e0665d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1795,6 +1795,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..9b6f8e73454 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -233,6 +233,7 @@ mod macro_use; mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; +mod manual_ok_or; mod manual_strip; mod manual_unwrap_or; mod map_clone; @@ -645,6 +646,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &manual_ok_or::MANUAL_OK_OR, &manual_strip::MANUAL_STRIP, &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, @@ -1140,6 +1142,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); + store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1229,6 +1232,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(&manual_ok_or::MANUAL_OK_OR), LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs new file mode 100644 index 00000000000..38298eb813a --- /dev/null +++ b/clippy_lints/src/manual_ok_or.rs @@ -0,0 +1,97 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that reimplement `Option::ok_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = None; + /// foo.ok_or("error"); + /// ``` + pub MANUAL_OK_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::ok_or`" +} + +declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); + +impl LateLintPass<'_> for ManualOkOr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { + if in_external_macro(cx.sess(), scrutinee.span) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind; + if method_segment.ident.name == sym!(map_or); + if args.len() == 3; + let method_receiver = &args[0]; + let ty = cx.typeck_results().expr_ty(method_receiver); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + let or_expr = &args[1]; + if is_ok_wrapping(cx, &args[2]); + if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; + if utils::match_qpath(err_path, &utils::paths::RESULT_ERR); + if let Some(method_receiver_snippet) = utils::snippet_opt(cx, method_receiver.span); + if let Some(err_arg_snippet) = utils::snippet_opt(cx, err_arg.span); + if let Some(indent) = utils::indent_of(cx, scrutinee.span); + then { + let reindented_err_arg_snippet = + utils::reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + utils::span_lint_and_sugg( + cx, + MANUAL_OK_OR, + scrutinee.span, + "this pattern reimplements `Option::ok_or`", + "replace with", + format!( + "{}.ok_or({})", + method_receiver_snippet, + reindented_err_arg_snippet + ), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { + if let ExprKind::Path(ref qpath) = map_expr.kind { + if utils::match_qpath(qpath, &utils::paths::RESULT_OK) { + return true; + } + } + if_chain! { + if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind; + let body = cx.tcx.hir().body(body_id); + if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; + if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; + if utils::match_qpath(ok_path, &utils::paths::RESULT_OK); + if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind; + if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res; + then { param_id == ok_arg_path_id } else { false } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..f438da00720 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1173,6 +1173,13 @@ vec![ deprecation: None, module: "manual_non_exhaustive", }, + Lint { + name: "manual_ok_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::ok_or`", + deprecation: None, + module: "manual_ok_or", + }, Lint { name: "manual_range_contains", group: "style", diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed new file mode 100644 index 00000000000..b42e94bd727 --- /dev/null +++ b/tests/ui/manual_ok_or.fixed @@ -0,0 +1,40 @@ +// run-rustfix +#![warn(clippy::manual_ok_or)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::redundant_closure)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +fn main() { + // basic case + let foo: Option = None; + foo.ok_or("error"); + + // eta expansion case + foo.ok_or("error"); + + // turbo fish syntax + None::.ok_or("error"); + + // multiline case + #[rustfmt::skip] + foo.ok_or(&format!( + "{}{}{}{}{}{}{}", + "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")); + + // not applicable, closure isn't direct `Ok` wrapping + foo.map_or(Err("error"), |v| Ok(v + 1)); + + // not applicable, or side isn't `Result::Err` + foo.map_or(Ok::(1), |v| Ok(v)); + + // not applicatble, expr is not a `Result` value + foo.map_or(42, |v| v); + + // TODO patterns not covered yet + match foo { + Some(v) => Ok(v), + None => Err("error"), + }; + foo.map_or_else(|| Err("error"), |v| Ok(v)); +} diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs new file mode 100644 index 00000000000..e5a6056fbf5 --- /dev/null +++ b/tests/ui/manual_ok_or.rs @@ -0,0 +1,44 @@ +// run-rustfix +#![warn(clippy::manual_ok_or)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::redundant_closure)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +fn main() { + // basic case + let foo: Option = None; + foo.map_or(Err("error"), |v| Ok(v)); + + // eta expansion case + foo.map_or(Err("error"), Ok); + + // turbo fish syntax + None::.map_or(Err("error"), |v| Ok(v)); + + // multiline case + #[rustfmt::skip] + foo.map_or(Err::( + &format!( + "{}{}{}{}{}{}{}", + "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer") + ), + |v| Ok(v), + ); + + // not applicable, closure isn't direct `Ok` wrapping + foo.map_or(Err("error"), |v| Ok(v + 1)); + + // not applicable, or side isn't `Result::Err` + foo.map_or(Ok::(1), |v| Ok(v)); + + // not applicatble, expr is not a `Result` value + foo.map_or(42, |v| v); + + // TODO patterns not covered yet + match foo { + Some(v) => Ok(v), + None => Err("error"), + }; + foo.map_or_else(|| Err("error"), |v| Ok(v)); +} diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr new file mode 100644 index 00000000000..8ea10ac5436 --- /dev/null +++ b/tests/ui/manual_ok_or.stderr @@ -0,0 +1,41 @@ +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:11:5 + | +LL | foo.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` + | + = note: `-D clippy::manual-ok-or` implied by `-D warnings` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:14:5 + | +LL | foo.map_or(Err("error"), Ok); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:17:5 + | +LL | None::.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:21:5 + | +LL | / foo.map_or(Err::( +LL | | &format!( +LL | | "{}{}{}{}{}{}{}", +LL | | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer") +LL | | ), +LL | | |v| Ok(v), +LL | | ); + | |_____^ + | +help: replace with + | +LL | foo.ok_or(&format!( +LL | "{}{}{}{}{}{}{}", +LL | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")); + | + +error: aborting due to 4 previous errors + From 66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 27 Oct 2020 07:42:13 +0200 Subject: [PATCH 0847/1110] Add `invalid_paths` internal lint --- clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 79 ++++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 25 ++++++++ tests/ui/invalid_paths.rs | 23 +++++++ tests/ui/invalid_paths.stderr | 16 +++++ 5 files changed, 146 insertions(+) create mode 100644 tests/ui/invalid_paths.rs create mode 100644 tests/ui/invalid_paths.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..7c8cb90fe1c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -892,6 +892,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, + &utils::internal_lints::INVALID_PATHS, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, @@ -919,6 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; @@ -1280,6 +1282,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), + LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index bfe426a25eb..6ca72d895c8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,3 +1,4 @@ +use crate::consts::{constant_simple, Constant}; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, @@ -14,9 +15,11 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -229,6 +232,21 @@ declare_clippy_lint! { "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" } +declare_clippy_lint! { + /// **What it does:** + /// Checks the paths module for invalid paths. + /// + /// **Why is this bad?** + /// It indicates a bug in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** None. + pub INVALID_PATHS, + internal, + "invalid path" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -761,3 +779,64 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, path: &[&str]) -> bool { + if path_to_res(cx, path).is_some() { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + for lang_item in lang_items.items() { + if let Some(def_id) = lang_item { + let lang_item_path = cx.get_def_path(*def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*def_id) { + if child.ident.name == *item { + return true; + } + } + } + } + } + } + + false +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path"); + } + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8297b9d128d..a1ecca0961a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -268,6 +268,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { krate: *krate, index: CRATE_DEF_INDEX, }; + let mut current_item = None; let mut items = cx.tcx.item_children(krate); let mut path_it = path.iter().skip(1).peekable(); @@ -277,6 +278,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { None => return None, }; + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + if segment.is_empty() { + continue; + } + let result = SmallVec::<[_; 8]>::new(); for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { if item.ident.name.as_str() == *segment { @@ -284,10 +291,28 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { return Some(item.res); } + current_item = Some(item); items = cx.tcx.item_children(item.res.def_id()); break; } } + + // The segment isn't a child_item. + // Try to find it under an inherent impl. + if_chain! { + if path_it.peek().is_none(); + if let Some(current_item) = current_item; + let item_def_id = current_item.res.def_id(); + if cx.tcx.def_kind(item_def_id) == DefKind::Struct; + then { + // Bad `find_map` suggestion. See #4193. + #[allow(clippy::find_map)] + return cx.tcx.inherent_impls(item_def_id).iter() + .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id)) + .find(|item| item.ident.name.as_str() == *segment) + .map(|item| item.res); + } + } } } else { None diff --git a/tests/ui/invalid_paths.rs b/tests/ui/invalid_paths.rs new file mode 100644 index 00000000000..01e28ae5e9d --- /dev/null +++ b/tests/ui/invalid_paths.rs @@ -0,0 +1,23 @@ +#![warn(clippy::internal)] + +mod paths { + // Good path + pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; + + // Path to method on inherent impl of a primitive type + pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; + + // Path to method on inherent impl + pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; + + // Path with empty segment + pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + + // Path with bad crate + pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + + // Path with bad module + pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; +} + +fn main() {} diff --git a/tests/ui/invalid_paths.stderr b/tests/ui/invalid_paths.stderr new file mode 100644 index 00000000000..bd69d661b71 --- /dev/null +++ b/tests/ui/invalid_paths.stderr @@ -0,0 +1,16 @@ +error: invalid path + --> $DIR/invalid_paths.rs:17:5 + | +LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::clippy-lints-internal` implied by `-D warnings` + +error: invalid path + --> $DIR/invalid_paths.rs:20:5 + | +LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From f79c4afd3a5c408bd9253311b224773702a912df Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 27 Oct 2020 07:43:38 +0200 Subject: [PATCH 0848/1110] Fix invalid paths --- clippy_lints/src/derive.rs | 10 +++++----- clippy_lints/src/float_equality_without_abs.rs | 6 ++++-- clippy_lints/src/utils/paths.rs | 16 ++++++++-------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index bf8e030cc29..c75efc6e99f 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; @@ -193,10 +193,9 @@ fn check_hash_peq<'tcx>( hash_is_automatically_derived: bool, ) { if_chain! { - if match_path(&trait_ref.path, &paths::HASH); if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait(); - if let Some(def_id) = &trait_ref.trait_def_id(); - if !def_id.is_local(); + if let Some(def_id) = trait_ref.trait_def_id(); + if match_def_path(cx, def_id, &paths::HASH); then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { @@ -352,7 +351,8 @@ fn check_unsafe_derive_deserialize<'tcx>( } if_chain! { - if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); + if let Some(trait_def_id) = trait_ref.trait_def_id(); + if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE); if let ty::Adt(def, _) = ty.kind(); if let Some(local_def_id) = def.did.as_local(); let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index 69818b4d3c6..c1c08597ee6 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,7 +1,8 @@ -use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; +use crate::utils::{match_def_path, paths, span_lint_and_then, sugg}; use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -76,7 +77,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { // right hand side matches either f32::EPSILON or f64::EPSILON if let ExprKind::Path(ref epsilon_path) = rhs.kind; - if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); + if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id); + if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON); // values of the substractions on the left hand side are of the type float let t_val_l = cx.typeck_results().expr_ty(val_l); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd9b92efe58..d5a0e0d1f29 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -32,10 +32,10 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; -pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; -pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"]; -pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"]; +pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; +pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; @@ -47,7 +47,7 @@ pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -pub const HASH: [&str; 2] = ["hash", "Hash"]; +pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; @@ -58,7 +58,7 @@ pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "Into pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; -pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"]; +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; @@ -86,8 +86,8 @@ pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; -pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; -pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; +pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; @@ -107,7 +107,7 @@ pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; -pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"]; +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; From 8bd1e23167c53b19aca837ffb3af6a58de3f036e Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 27 Oct 2020 23:32:38 +0900 Subject: [PATCH 0849/1110] Replace `E-easy` with `good first issue` in `CONTRIBUTING.md` `E-easy` isn't used, so `good first issue` is more appropriate. --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6494695606c..abb82c46935 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,8 +63,8 @@ To figure out how this syntax structure is encoded in the AST, it is recommended Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. -[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an E-easy issue first. -They are mostly classified as [`E-medium`], since they might be somewhat involved code wise, +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] +first. They are mostly classified as [`E-medium`], since they might be somewhat involved code wise, but not difficult per-se. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a From 09e70536075fd92506ef985c5e662e1b2be3dd3d Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 24 Oct 2020 18:05:02 +0300 Subject: [PATCH 0850/1110] simplify SpanlessEq::eq_path_segment --- clippy_lints/src/utils/hir_utils.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index c9e639e8728..e4ad105c351 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -261,14 +261,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { // The == of idents doesn't work with different contexts, // we have to be explicit about hygiene - if left.ident.as_str() != right.ident.as_str() { - return false; - } - match (&left.args, &right.args) { - (&None, &None) => true, - (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r), - _ => false, - } + left.ident.as_str() == right.ident.as_str() + && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { From 2b7dd313685279fce06f86bd739d798792135331 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 24 Oct 2020 18:06:07 +0300 Subject: [PATCH 0851/1110] improve MATCH_LIKE_MATCHES_MACRO lint - add tests - refactor match_same_arms lint - prioritize match_expr_like_matches_macro over match_same_arms --- clippy_lints/src/copies.rs | 215 +---------------- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/matches.rs | 217 ++++++++++++++++-- clippy_lints/src/utils/mod.rs | 38 +++ src/lintlist/mod.rs | 2 +- tests/ui/match_expr_like_matches_macro.fixed | 68 +++++- tests/ui/match_expr_like_matches_macro.rs | 76 +++++- tests/ui/match_expr_like_matches_macro.stderr | 24 +- tests/ui/match_same_arms2.rs | 16 ++ tests/ui/match_same_arms2.stderr | 15 +- 10 files changed, 439 insertions(+), 236 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 6c969c3ead0..46ce92ea6d7 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,13 +1,8 @@ -use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; +use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; +use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note}; +use rustc_hir::{Block, Expr}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; -use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -108,48 +103,7 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_clippy_lint! { - /// **What it does:** Checks for `match` with identical arm bodies. - /// - /// **Why is this bad?** This is probably a copy & paste error. If arm bodies - /// are the same on purpose, you can factor them - /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). - /// - /// **Known problems:** False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// - /// **Example:** - /// ```rust,ignore - /// match foo { - /// Bar => bar(), - /// Quz => quz(), - /// Baz => bar(), // <= oops - /// } - /// ``` - /// - /// This should probably be - /// ```rust,ignore - /// match foo { - /// Bar => bar(), - /// Quz => quz(), - /// Baz => baz(), // <= fixed - /// } - /// ``` - /// - /// or if the original code was not a typo: - /// ```rust,ignore - /// match foo { - /// Bar | Baz => bar(), // <= shows the intent better - /// Quz => quz(), - /// } - /// ``` - pub MATCH_SAME_ARMS, - pedantic, - "`match` with identical arm bodies" -} - -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]); +declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -167,7 +121,6 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { lint_same_then_else(cx, &blocks); lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); - lint_match_arms(cx, expr); } } } @@ -243,122 +196,6 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { } } -/// Implementation of `MATCH_SAME_ARMS`. -fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { - fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { - lhs.len() == rhs.len() - && lhs - .iter() - .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) - } - - if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(&arm.body); - h.finish() - }; - - let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { - let min_index = usize::min(lindex, rindex); - let max_index = usize::max(lindex, rindex); - - // Arms with a guard are ignored, those can’t always be merged together - // This is also the case for arms in-between each there is an arm with a guard - (min_index..=max_index).all(|index| arms[index].guard.is_none()) && - SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && - // all patterns should have the same bindings - same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) - }; - - let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { - span_lint_and_then( - cx, - MATCH_SAME_ARMS, - j.body.span, - "this `match` has identical arm bodies", - |diag| { - diag.span_note(i.body.span, "same as this"); - - // Note: this does not use `span_suggestion` on purpose: - // there is no clean way - // to remove the other arm. Building a span and suggest to replace it to "" - // makes an even more confusing error message. Also in order not to make up a - // span for the whole pattern, the suggestion is only shown when there is only - // one pattern. The user should know about `|` if they are already using it… - - let lhs = snippet(cx, i.pat.span, ""); - let rhs = snippet(cx, j.pat.span, ""); - - if let PatKind::Wild = j.pat.kind { - // if the last arm is _, then i could be integrated into _ - // note that i.pat cannot be _, because that would mean that we're - // hiding all the subsequent arms, and rust won't compile - diag.span_note( - i.body.span, - &format!( - "`{}` has the same arm body as the `_` wildcard, consider removing it", - lhs - ), - ); - } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); - } - }, - ); - } - } -} - -/// Returns the list of bindings in a pattern. -fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap> { - fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { - match pat.kind { - PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), - PatKind::TupleStruct(_, pats, _) => { - for pat in pats { - bindings_impl(cx, pat, map); - } - }, - PatKind::Binding(.., ident, ref as_pat) => { - if let Entry::Vacant(v) = map.entry(ident.name) { - v.insert(cx.typeck_results().pat_ty(pat)); - } - if let Some(ref as_pat) = *as_pat { - bindings_impl(cx, as_pat, map); - } - }, - PatKind::Or(fields) | PatKind::Tuple(fields, _) => { - for pat in fields { - bindings_impl(cx, pat, map); - } - }, - PatKind::Struct(_, fields, _) => { - for pat in fields { - bindings_impl(cx, &pat.pat, map); - } - }, - PatKind::Slice(lhs, ref mid, rhs) => { - for pat in lhs { - bindings_impl(cx, pat, map); - } - if let Some(ref mid) = *mid { - bindings_impl(cx, mid, map); - } - for pat in rhs { - bindings_impl(cx, pat, map); - } - }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), - } - } - - let mut result = FxHashMap::default(); - bindings_impl(cx, pat, &mut result); - result -} - fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> where Eq: Fn(&T, &T) -> bool, @@ -370,47 +207,3 @@ where } None } - -fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)> -where - Eq: Fn(&T, &T) -> bool, -{ - if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { - Some((&exprs[0], &exprs[1])) - } else { - None - } -} - -fn search_same(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> -where - Hash: Fn(&T) -> u64, - Eq: Fn(&T, &T) -> bool, -{ - if let Some(expr) = search_common_cases(&exprs, &eq) { - return vec![expr]; - } - - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: FxHashMap<_, Vec<&_>> = - FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); - - for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } - } - o.get_mut().push(expr); - }, - Entry::Vacant(v) => { - v.insert(vec![expr]); - }, - } - } - - match_expr_list -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7c8cb90fe1c..459fdd70443 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -528,7 +528,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &comparison_chain::COMPARISON_CHAIN, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, - &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, @@ -659,6 +658,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::MATCH_LIKE_MATCHES_MACRO, &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, + &matches::MATCH_SAME_ARMS, &matches::MATCH_SINGLE_BINDING, &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, @@ -1204,7 +1204,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), - LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), @@ -1234,6 +1233,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), + LintId::of(&matches::MATCH_SAME_ARMS), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH_ELSE), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d93433c607f..4bdfca1a292 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1,5 +1,4 @@ use crate::consts::{constant, miri_to_const, Constant}; -use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ @@ -8,8 +7,10 @@ use crate::utils::{ snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; +use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ @@ -18,10 +19,12 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; +use rustc_span::Symbol; use std::cmp::Ordering; +use std::collections::hash_map::Entry; use std::collections::Bound; declare_clippy_lint! { @@ -475,6 +478,47 @@ declare_clippy_lint! { "a match that could be written with the matches! macro" } +declare_clippy_lint! { + /// **What it does:** Checks for `match` with identical arm bodies. + /// + /// **Why is this bad?** This is probably a copy & paste error. If arm bodies + /// are the same on purpose, you can factor them + /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). + /// + /// **Known problems:** False positive possible with order dependent `match` + /// (see issue + /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). + /// + /// **Example:** + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => bar(), // <= oops + /// } + /// ``` + /// + /// This should probably be + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => baz(), // <= fixed + /// } + /// ``` + /// + /// or if the original code was not a typo: + /// ```rust,ignore + /// match foo { + /// Bar | Baz => bar(), // <= shows the intent better + /// Quz => quz(), + /// } + /// ``` + pub MATCH_SAME_ARMS, + pedantic, + "`match` with identical arm bodies" +} + #[derive(Default)] pub struct Matches { infallible_destructuring_match_linted: bool, @@ -495,7 +539,8 @@ impl_lint_pass!(Matches => [ INFALLIBLE_DESTRUCTURING_MATCH, REST_PAT_IN_FULLY_BOUND_STRUCTS, REDUNDANT_PATTERN_MATCHING, - MATCH_LIKE_MATCHES_MACRO + MATCH_LIKE_MATCHES_MACRO, + MATCH_SAME_ARMS, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -505,7 +550,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check(cx, expr); - check_match_like_matches(cx, expr); + if !check_match_like_matches(cx, expr) { + lint_match_arms(cx, expr); + } if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); @@ -1063,32 +1110,47 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { } /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` -fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), - _ => return, + _ => false, } + } else { + false } } /// Lint a `match` or desugared `if let` for replacement by `matches!` -fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) { +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool { if_chain! { - if arms.len() == 2; + if arms.len() >= 2; if cx.typeck_results().expr_ty(expr).is_bool(); - if is_wild(&arms[1].pat); - if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); - if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); - if first != second; + if let Some((b1_arm, b0_arms)) = arms.split_last(); + if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared); + if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared); + if is_wild(&b1_arm.pat); + if b0 != b1; + let if_guard = &b0_arms[0].guard; + if if_guard.is_none() || b0_arms.len() == 1; + if b0_arms[1..].iter() + .all(|arm| { + find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && + arm.guard.is_none() + }); then { let mut applicability = Applicability::MachineApplicable; - - let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard { - format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability)) + let pat = { + use itertools::Itertools as _; + b0_arms.iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) + .join(" | ") + }; + let pat_and_guard = if let Some(Guard::If(g)) = if_guard { + format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { - format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability)) + pat }; span_lint_and_sugg( cx, @@ -1098,12 +1160,15 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr "try this", format!( "{}matches!({}, {})", - if first { "" } else { "!" }, + if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex.span, "..", &mut applicability), pat_and_guard, ), applicability, - ) + ); + true + } else { + false } } } @@ -1657,3 +1722,119 @@ fn test_overlapping() { ],) ); } + +/// Implementation of `MATCH_SAME_ARMS`. +fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { + fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { + lhs.len() == rhs.len() + && lhs + .iter() + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) + } + + if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_expr(&arm.body); + h.finish() + }; + + let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { + let min_index = usize::min(lindex, rindex); + let max_index = usize::max(lindex, rindex); + + // Arms with a guard are ignored, those can’t always be merged together + // This is also the case for arms in-between each there is an arm with a guard + (min_index..=max_index).all(|index| arms[index].guard.is_none()) && + SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && + // all patterns should have the same bindings + same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) + }; + + let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); + for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + j.body.span, + "this `match` has identical arm bodies", + |diag| { + diag.span_note(i.body.span, "same as this"); + + // Note: this does not use `span_suggestion` on purpose: + // there is no clean way + // to remove the other arm. Building a span and suggest to replace it to "" + // makes an even more confusing error message. Also in order not to make up a + // span for the whole pattern, the suggestion is only shown when there is only + // one pattern. The user should know about `|` if they are already using it… + + let lhs = snippet(cx, i.pat.span, ""); + let rhs = snippet(cx, j.pat.span, ""); + + if let PatKind::Wild = j.pat.kind { + // if the last arm is _, then i could be integrated into _ + // note that i.pat cannot be _, because that would mean that we're + // hiding all the subsequent arms, and rust won't compile + diag.span_note( + i.body.span, + &format!( + "`{}` has the same arm body as the `_` wildcard, consider removing it", + lhs + ), + ); + } else { + diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); + } + }, + ); + } + } +} + +/// Returns the list of bindings in a pattern. +fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap> { + fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { + match pat.kind { + PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), + PatKind::TupleStruct(_, pats, _) => { + for pat in pats { + bindings_impl(cx, pat, map); + } + }, + PatKind::Binding(.., ident, ref as_pat) => { + if let Entry::Vacant(v) = map.entry(ident.name) { + v.insert(cx.typeck_results().pat_ty(pat)); + } + if let Some(ref as_pat) = *as_pat { + bindings_impl(cx, as_pat, map); + } + }, + PatKind::Or(fields) | PatKind::Tuple(fields, _) => { + for pat in fields { + bindings_impl(cx, pat, map); + } + }, + PatKind::Struct(_, fields, _) => { + for pat in fields { + bindings_impl(cx, &pat.pat, map); + } + }, + PatKind::Slice(lhs, ref mid, rhs) => { + for pat in lhs { + bindings_impl(cx, pat, map); + } + if let Some(ref mid) = *mid { + bindings_impl(cx, mid, map); + } + for pat in rhs { + bindings_impl(cx, pat, map); + } + }, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), + } + } + + let mut result = FxHashMap::default(); + bindings_impl(cx, pat, &mut result); + result +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a1ecca0961a..0a8a4a5f9ae 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -27,11 +27,14 @@ pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; +use std::collections::hash_map::Entry; +use std::hash::BuildHasherDefault; use std::mem; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -1465,6 +1468,41 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> +where + Hash: Fn(&T) -> u64, + Eq: Fn(&T, &T) -> bool, +{ + if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { + return vec![(&exprs[0], &exprs[1])]; + } + + let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); + + let mut map: FxHashMap<_, Vec<&_>> = + FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + + for expr in exprs { + match map.entry(hash(expr)) { + Entry::Occupied(mut o) => { + for o in o.get() { + if eq(o, expr) { + match_expr_list.push((o, expr)); + } + } + o.get_mut().push(expr); + }, + Entry::Vacant(v) => { + v.insert(vec![expr]); + }, + } + } + + match_expr_list +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..aba436e5c02 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1304,7 +1304,7 @@ vec![ group: "pedantic", desc: "`match` with identical arm bodies", deprecation: None, - module: "copies", + module: "matches", }, Lint { name: "match_single_binding", diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index f3e19092480..7f4ebf56673 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns)] +#![allow(unreachable_patterns, dead_code)] fn main() { let x = Some(5); @@ -33,4 +33,70 @@ fn main() { _ => true, None => false, }; + + enum E { + A(u32), + B(i32), + C, + D, + }; + let x = E::A(2); + { + // lint + let _ans = matches!(x, E::A(_) | E::B(_)); + } + { + // lint + let _ans = !matches!(x, E::B(_) | E::C); + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => false, + E::C => true, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => true, + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) if a < 10 => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) => a == 10, + E::B(_) => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => true, + _ => false, + }; + } } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index fbae7c18b92..aee56dd4a5e 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns)] +#![allow(unreachable_patterns, dead_code)] fn main() { let x = Some(5); @@ -45,4 +45,78 @@ fn main() { _ => true, None => false, }; + + enum E { + A(u32), + B(i32), + C, + D, + }; + let x = E::A(2); + { + // lint + let _ans = match x { + E::A(_) => true, + E::B(_) => true, + _ => false, + }; + } + { + // lint + let _ans = match x { + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => false, + E::C => true, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => true, + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) if a < 10 => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) => a == 10, + E::B(_) => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => true, + _ => false, + }; + } } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 4668f8565a6..c52e41c7889 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -48,5 +48,27 @@ error: if let .. else expression looks like `matches!` macro LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` -error: aborting due to 5 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:58:20 + | +LL | let _ans = match x { + | ____________________^ +LL | | E::A(_) => true, +LL | | E::B(_) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:66:20 + | +LL | let _ans = match x { + | ____________________^ +LL | | E::B(_) => false, +LL | | E::C => false, +LL | | _ => true, +LL | | }; + | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` + +error: aborting due to 7 previous errors diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index e1401d2796a..06d91497242 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -119,6 +119,22 @@ fn match_same_arms() { unreachable!(); }, } + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; } fn main() {} diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 26c65f32ad7..fccaf805616 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -141,5 +141,18 @@ LL | Ok(3) => println!("ok"), | ^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 7 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_same_arms2.rs:133:16 + | +LL | let _ans = match x { + | ________________^ +LL | | E::A => false, +LL | | E::B => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `!matches!(x, E::A | E::B)` + | + = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` + +error: aborting due to 8 previous errors From 4c58860cbfe51049d48f4f31f76dcc18b39cfe98 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 27 Oct 2020 23:18:08 +0900 Subject: [PATCH 0852/1110] Fix suggestion to add unneeded space in `unused_unit` --- clippy_lints/src/unused_unit.rs | 11 ++++++ tests/ui/unused_unit.fixed | 31 +++++++++------ tests/ui/unused_unit.rs | 9 +++++ tests/ui/unused_unit.stderr | 68 +++++++++++++++++++++------------ 4 files changed, 83 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 7548c6afa97..b1339c3d639 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -123,6 +123,17 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { fn_source .rfind("->") .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + let mut rpos = rpos; + let chars: Vec = fn_source.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } ( #[allow(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 07f2791786d..7afc5361356 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -15,35 +15,35 @@ struct Unitter; impl Unitter { #[allow(clippy::no_effect)] - pub fn get_unit(&self, f: F, _g: G) - where G: Fn() { - let _y: &dyn Fn() = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } impl Into<()> for Unitter { #[rustfmt::skip] - fn into(self) { + fn into(self) { } } trait Trait { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() ; + G: FnMut(), + H: Fn(); } impl Trait for Unitter { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() {} + G: FnMut(), + H: Fn() {} } -fn return_unit() { } +fn return_unit() { } #[allow(clippy::needless_return)] #[allow(clippy::never_loop)] @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test(){} + +#[rustfmt::skip] +fn test2(){} + +#[rustfmt::skip] +fn test3(){} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index e2c6afb020f..96cef1ed5a5 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test()->(){} + +#[rustfmt::skip] +fn test2() ->(){} + +#[rustfmt::skip] +fn test3()-> (){} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 81e6738e6bf..c45634c2b6d 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:29 + --> $DIR/unused_unit.rs:18:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -11,28 +11,28 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:19 + --> $DIR/unused_unit.rs:19:18 | LL | where G: Fn() -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:59 + --> $DIR/unused_unit.rs:18:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:27 + --> $DIR/unused_unit.rs:20:26 | LL | let _y: &dyn Fn() -> () = &f; - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:19 + --> $DIR/unused_unit.rs:27:18 | LL | fn into(self) -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:28:9 @@ -41,46 +41,46 @@ LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:30 + --> $DIR/unused_unit.rs:33:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:35:20 + --> $DIR/unused_unit.rs:35:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:17 + --> $DIR/unused_unit.rs:36:16 | LL | H: Fn() -> (); - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:30 + --> $DIR/unused_unit.rs:40:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:42:20 + --> $DIR/unused_unit.rs:42:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:17 + --> $DIR/unused_unit.rs:43:16 | LL | H: Fn() -> () {} - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:18 + --> $DIR/unused_unit.rs:46:17 | LL | fn return_unit() -> () { () } - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:46:26 @@ -100,5 +100,23 @@ error: unneeded `()` LL | return(); | ^^ help: remove the `()` -error: aborting due to 16 previous errors +error: unneeded unit return type + --> $DIR/unused_unit.rs:75:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:78:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:81:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors From dace756c6fa8793badbc9b872047cc539952e718 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 28 Oct 2020 14:25:41 +0900 Subject: [PATCH 0853/1110] cargo dev update_lints --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..d93efcafb19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1962,7 +1962,6 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment -[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo From ffc2e666710cf5b6b97a162dfa01fc1fe4452af2 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 28 Oct 2020 14:36:29 +0900 Subject: [PATCH 0854/1110] Fix reference --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d93efcafb19..d10aefa2042 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1552,7 +1552,7 @@ Released 2018-09-13 ## 0.0.64 — 2016-04-26 * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* -* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`] +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* From c42a22d2dcae233b43e1d8b8a2f90dd580ca2136 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 29 Oct 2020 01:02:09 +0900 Subject: [PATCH 0855/1110] Use `double_neg.stderr` --- .github/driver.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/driver.sh b/.github/driver.sh index fc4dca5042b..e9054c459ed 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -22,9 +22,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cast.rs 2> cast.stderr && exit 1 -sed -e "s,tests/ui,\$DIR," -e "/= help/d" cast.stderr > normalized.stderr -diff normalized.stderr tests/ui/cast.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2> double_neg.stderr && exit 1 +sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr > normalized.stderr +diff normalized.stderr tests/ui/double_neg.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same From a50d9e7af641d52e066b111014993b042b0be5a7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 28 Oct 2020 22:32:13 +0100 Subject: [PATCH 0856/1110] Deprecate temporary_cstr_as_ptr --- CHANGELOG.md | 1 + clippy_lints/src/deprecated_lints.rs | 9 +++++++++ clippy_lints/src/lib.rs | 4 ++++ tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +++++++- 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d10aefa2042..aa47930b520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1962,6 +1962,7 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index c5884361dff..461c6e31d3e 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -172,3 +172,12 @@ declare_deprecated_lint! { pub DROP_BOUNDS, "this lint has been uplifted to rustc and is now called `drop_bounds`" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been uplifted to rustc and is now called + /// `temporary_cstring_as_ptr`. + pub TEMPORARY_CSTRING_AS_PTR, + "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb949345945..2d374969846 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -488,6 +488,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::drop_bounds", "this lint has been uplifted to rustc and is now called `drop_bounds`", ); + store.register_removed( + "clippy::temporary_cstring_as_ptr", + "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 9e32fe36ece..56755596c97 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -9,5 +9,6 @@ #[warn(clippy::unused_label)] #[warn(clippy::regex_macro)] #[warn(clippy::drop_bounds)] +#[warn(clippy::temporary_cstring_as_ptr)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index d3400a7be09..37b726fc00f 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -66,11 +66,17 @@ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted LL | #[warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`` + --> $DIR/deprecated.rs:12:8 + | +LL | #[warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors From e83e79f1c21f7bf08b639c9f3cb04b38b7fecf81 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 28 Oct 2020 22:36:22 +0100 Subject: [PATCH 0857/1110] Reinstate link to temporary_cstr_as_ptr --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa47930b520..25f3b5da198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1552,7 +1552,7 @@ Released 2018-09-13 ## 0.0.64 — 2016-04-26 * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* -* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] +* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* From 0d6eed1b1f74c31fdc6a8ce99570505dcb340e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 28 Oct 2020 23:19:04 +0100 Subject: [PATCH 0858/1110] use diff -u in driver.sh test this changs the add/delete indication from > > < to + + - (same as git diff) --- .github/driver.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/driver.sh b/.github/driver.sh index e9054c459ed..561954e7c3a 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -24,18 +24,18 @@ unset CARGO_MANIFEST_DIR # FIXME: How to match the clippy invocation in compile-test.rs? ./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2> double_neg.stderr && exit 1 sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr > normalized.stderr -diff normalized.stderr tests/ui/double_neg.stderr +diff -u normalized.stderr tests/ui/double_neg.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=`rustc --print sysroot` -diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) +diff -u <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) echo "fn main() {}" > target/driver_test.rs # we can't run 2 rustcs on the same file at the same time CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc` RUSTC=`rustc ./target/driver_test.rs` -diff <($CLIPPY) <($RUSTC) +diff -u <($CLIPPY) <($RUSTC) # TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR From abd64d7c054ece769abff0cd90374789f2ecf639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 28 Oct 2020 14:45:21 +0100 Subject: [PATCH 0859/1110] add a couple of ICE testcases Fixes #6250 Fixes #6251 Fixes #6252 Fixes #6255 Fixes #6256 --- tests/ui/crashes/ice-6250.rs | 16 +++++++++++ tests/ui/crashes/ice-6250.stderr | 27 +++++++++++++++++++ tests/ui/crashes/ice-6251.rs | 6 +++++ tests/ui/crashes/ice-6251.stderr | 43 +++++++++++++++++++++++++++++ tests/ui/crashes/ice-6252.rs | 15 +++++++++++ tests/ui/crashes/ice-6252.stderr | 46 ++++++++++++++++++++++++++++++++ tests/ui/crashes/ice-6254.rs | 15 +++++++++++ tests/ui/crashes/ice-6254.stderr | 12 +++++++++ tests/ui/crashes/ice-6255.rs | 15 +++++++++++ tests/ui/crashes/ice-6255.stderr | 13 +++++++++ tests/ui/crashes/ice-6256.rs | 13 +++++++++ tests/ui/crashes/ice-6256.stderr | 18 +++++++++++++ 12 files changed, 239 insertions(+) create mode 100644 tests/ui/crashes/ice-6250.rs create mode 100644 tests/ui/crashes/ice-6250.stderr create mode 100644 tests/ui/crashes/ice-6251.rs create mode 100644 tests/ui/crashes/ice-6251.stderr create mode 100644 tests/ui/crashes/ice-6252.rs create mode 100644 tests/ui/crashes/ice-6252.stderr create mode 100644 tests/ui/crashes/ice-6254.rs create mode 100644 tests/ui/crashes/ice-6254.stderr create mode 100644 tests/ui/crashes/ice-6255.rs create mode 100644 tests/ui/crashes/ice-6255.stderr create mode 100644 tests/ui/crashes/ice-6256.rs create mode 100644 tests/ui/crashes/ice-6256.stderr diff --git a/tests/ui/crashes/ice-6250.rs b/tests/ui/crashes/ice-6250.rs new file mode 100644 index 00000000000..c33580ff6ab --- /dev/null +++ b/tests/ui/crashes/ice-6250.rs @@ -0,0 +1,16 @@ +// originally from glacier/fixed/77218.rs +// ice while adjusting... + +pub struct Cache { + data: Vec, +} + +pub fn list_data(cache: &Cache, key: usize) { + for reference in vec![1, 2, 3] { + if + /* let */ + Some(reference) = cache.data.get(key) { + unimplemented!() + } + } +} diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr new file mode 100644 index 00000000000..8241dcd8feb --- /dev/null +++ b/tests/ui/crashes/ice-6250.stderr @@ -0,0 +1,27 @@ +error[E0601]: `main` function not found in crate `ice_6250` + --> $DIR/ice-6250.rs:4:1 + | +LL | / pub struct Cache { +LL | | data: Vec, +LL | | } +LL | | +... | +LL | | } +LL | | } + | |_^ consider adding a `main` function to `$DIR/ice-6250.rs` + +error[E0308]: mismatched types + --> $DIR/ice-6250.rs:12:9 + | +LL | Some(reference) = cache.data.get(key) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | let Some(reference) = cache.data.get(key) { + | ^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0601. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/crashes/ice-6251.rs b/tests/ui/crashes/ice-6251.rs new file mode 100644 index 00000000000..6aa779aaeb3 --- /dev/null +++ b/tests/ui/crashes/ice-6251.rs @@ -0,0 +1,6 @@ +// originally from glacier/fixed/77329.rs +// assertion failed: `(left == right) ; different DefIds + +fn bug() -> impl Iterator { + std::iter::empty() +} diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr new file mode 100644 index 00000000000..9a7cf4b0919 --- /dev/null +++ b/tests/ui/crashes/ice-6251.stderr @@ -0,0 +1,43 @@ +error[E0601]: `main` function not found in crate `ice_6251` + --> $DIR/ice-6251.rs:4:1 + | +LL | / fn bug() -> impl Iterator { +LL | | std::iter::empty() +LL | | } + | |_^ consider adding a `main` function to `$DIR/ice-6251.rs` + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/ice-6251.rs:4:45 + | +LL | fn bug() -> impl Iterator { + | ^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn bug() -> impl Iterator { + | ^ + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/ice-6251.rs:4:54 + | +LL | fn bug() -> impl Iterator { + | ^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: the return type of a function must have a statically known size + +error[E0308]: mismatched types + --> $DIR/ice-6251.rs:4:44 + | +LL | fn bug() -> impl Iterator { + | ^^^^^^^^^^^ expected `usize`, found closure + | + = note: expected type `usize` + found closure `[closure@$DIR/ice-6251.rs:4:44: 4:55]` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0601. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/crashes/ice-6252.rs b/tests/ui/crashes/ice-6252.rs new file mode 100644 index 00000000000..2e3d9fd1e92 --- /dev/null +++ b/tests/ui/crashes/ice-6252.rs @@ -0,0 +1,15 @@ +// originally from glacier fixed/77919.rs +// encountered errors resolving bounds after type-checking + +trait TypeVal { + const VAL: T; +} +struct Five; +struct Multiply { + _n: PhantomData, +} +impl TypeVal for Multiply where N: TypeVal {} + +fn main() { + [1; >::VAL]; +} diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr new file mode 100644 index 00000000000..440973e2439 --- /dev/null +++ b/tests/ui/crashes/ice-6252.stderr @@ -0,0 +1,46 @@ +error[E0412]: cannot find type `PhantomData` in this scope + --> $DIR/ice-6252.rs:9:9 + | +LL | _n: PhantomData, + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::marker::PhantomData; + | + +error[E0412]: cannot find type `VAL` in this scope + --> $DIR/ice-6252.rs:11:63 + | +LL | impl TypeVal for Multiply where N: TypeVal {} + | - ^^^ not found in this scope + | | + | help: you might be missing a type parameter: `, VAL` + +error[E0046]: not all trait items implemented, missing: `VAL` + --> $DIR/ice-6252.rs:11:1 + | +LL | const VAL: T; + | ------------- `VAL` from trait +... +LL | impl TypeVal for Multiply where N: TypeVal {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation + +error: any use of this value will cause an error + --> $DIR/ice-6252.rs:5:5 + | +LL | const VAL: T; + | ^^^^^^^^^^^^^ no MIR body is available for DefId(0:5 ~ ice_6252[317d]::TypeVal::VAL) + | + = note: `#[deny(const_err)]` on by default + +error[E0080]: evaluation of constant value failed + --> $DIR/ice-6252.rs:14:9 + | +LL | [1; >::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0046, E0080, E0412. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs new file mode 100644 index 00000000000..c19eca43884 --- /dev/null +++ b/tests/ui/crashes/ice-6254.rs @@ -0,0 +1,15 @@ +// originally from ./src/test/ui/pattern/usefulness/consts-opaque.rs +// panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', +// compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 + +#[derive(PartialEq)] +struct Foo(i32); +const FOO_REF_REF: &&Foo = &&Foo(42); + +fn main() { + // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071) + match FOO_REF_REF { + FOO_REF_REF => {}, + Foo(_) => {}, + } +} diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr new file mode 100644 index 00000000000..95ebf23d818 --- /dev/null +++ b/tests/ui/crashes/ice-6254.stderr @@ -0,0 +1,12 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/ice-6254.rs:12:9 + | +LL | FOO_REF_REF => {}, + | ^^^^^^^^^^^ + | + = note: `-D indirect-structural-match` implied by `-D warnings` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-6255.rs b/tests/ui/crashes/ice-6255.rs new file mode 100644 index 00000000000..bd4a81d98e2 --- /dev/null +++ b/tests/ui/crashes/ice-6255.rs @@ -0,0 +1,15 @@ +// originally from rustc ./src/test/ui/macros/issue-78325-inconsistent-resolution.rs +// inconsistent resolution for a macro + +macro_rules! define_other_core { + ( ) => { + extern crate std as core; + //~^ ERROR macro-expanded `extern crate` items cannot shadow names passed with `--extern` + }; +} + +fn main() { + core::panic!(); +} + +define_other_core!(); diff --git a/tests/ui/crashes/ice-6255.stderr b/tests/ui/crashes/ice-6255.stderr new file mode 100644 index 00000000000..d973ea1e23a --- /dev/null +++ b/tests/ui/crashes/ice-6255.stderr @@ -0,0 +1,13 @@ +error: macro-expanded `extern crate` items cannot shadow names passed with `--extern` + --> $DIR/ice-6255.rs:6:9 + | +LL | extern crate std as core; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | define_other_core!(); + | --------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs new file mode 100644 index 00000000000..6f60d45d68a --- /dev/null +++ b/tests/ui/crashes/ice-6256.rs @@ -0,0 +1,13 @@ +// originally from rustc ./src/test/ui/regions/issue-78262.rs +// ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() + +trait TT {} + +impl dyn TT { + fn func(&self) {} +} + +fn main() { + let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types + //[nll]~^ ERROR: borrowed data escapes outside of closure +} diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr new file mode 100644 index 00000000000..0e8353a418a --- /dev/null +++ b/tests/ui/crashes/ice-6256.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/ice-6256.rs:11:28 + | +LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types + | ^^^^ lifetime mismatch + | + = note: expected reference `&(dyn TT + 'static)` + found reference `&dyn TT` +note: the anonymous lifetime #1 defined on the body at 11:13... + --> $DIR/ice-6256.rs:11:13 + | +LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 7f3462aa89b3ba570334fe9274a467be03367c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 29 Oct 2020 03:22:02 +0100 Subject: [PATCH 0860/1110] cargo dev ra-setup: don't inject deps multiple times if we have already done so Fixes #6220 --- clippy_dev/src/ra_setup.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index c67efc10f15..9d9e836cc08 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -11,7 +11,7 @@ use std::path::PathBuf; // code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details pub fn run(rustc_path: Option<&str>) { - // we can unwrap here because the arg is required here + // we can unwrap here because the arg is required by clap let rustc_path = PathBuf::from(rustc_path.unwrap()); assert!(rustc_path.is_dir(), "path is not a directory"); let rustc_source_basedir = rustc_path.join("compiler"); @@ -49,6 +49,15 @@ fn inject_deps_into_manifest( cargo_toml: &str, lib_rs: &str, ) -> std::io::Result<()> { + // do not inject deps if we have aleady done so + if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { + eprintln!( + "cargo dev ra-setup: warning: deps already found inside {}, doing nothing.", + manifest_path + ); + return Ok(()); + } + let extern_crates = lib_rs .lines() // get the deps From e97602e4825fd5a01834d7b0e218703dcc82c091 Mon Sep 17 00:00:00 2001 From: henil Date: Mon, 26 Oct 2020 19:28:22 +0530 Subject: [PATCH 0861/1110] Update existing arithmetic lint and add new tests related to it. --- clippy_lints/src/arithmetic.rs | 30 ++++++++- tests/ui/integer_arithmetic.rs | 10 +++ tests/ui/integer_arithmetic.stderr | 100 +++++++++++++++++++++-------- 3 files changed, 112 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index e84481f9b53..b1cdbab6de9 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -88,9 +88,33 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_span = Some(expr.span); - } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + match op.node { + hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { + hir::ExprKind::Lit(lit) => { + if let rustc_ast::ast::LitKind::Int(0, _) = lit.node { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } + }, + hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => { + if let hir::ExprKind::Lit(lit) = &expr.kind { + if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } + } + }, + _ => { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + }, + }, + _ => { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + }, + } + } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_span = Some(expr.span); } diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs index 7b1b64f390a..b74c93dc4a6 100644 --- a/tests/ui/integer_arithmetic.rs +++ b/tests/ui/integer_arithmetic.rs @@ -11,6 +11,8 @@ #[rustfmt::skip] fn main() { let mut i = 1i32; + let mut var1 = 0i32; + let mut var2 = -1i32; 1 + i; i * 2; 1 % @@ -32,7 +34,15 @@ fn main() { i -= 1; i *= 2; i /= 2; + i /= 0; + i /= -1; + i /= var1; + i /= var2; i %= 2; + i %= 0; + i %= -1; + i %= var1; + i %= var2; i <<= 3; i >>= 2; diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index 83e8a9cde3f..20356afc358 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -1,5 +1,19 @@ +error: this operation will panic at runtime + --> $DIR/integer_arithmetic.rs:37:5 + | +LL | i /= 0; + | ^^^^^^ attempt to divide `_` by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/integer_arithmetic.rs:42:5 + | +LL | i %= 0; + | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero + error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:14:5 + --> $DIR/integer_arithmetic.rs:16:5 | LL | 1 + i; | ^^^^^ @@ -7,125 +21,161 @@ LL | 1 + i; = note: `-D clippy::integer-arithmetic` implied by `-D warnings` error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:15:5 + --> $DIR/integer_arithmetic.rs:17:5 | LL | i * 2; | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:16:5 + --> $DIR/integer_arithmetic.rs:18:5 | LL | / 1 % LL | | i / 2; // no error, this is part of the expression in the preceding line - | |_________^ + | |_____^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:18:5 + --> $DIR/integer_arithmetic.rs:20:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:19:5 + --> $DIR/integer_arithmetic.rs:21:5 | LL | -i; | ^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:20:5 + --> $DIR/integer_arithmetic.rs:22:5 | LL | i >> 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:21:5 + --> $DIR/integer_arithmetic.rs:23:5 | LL | i << 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:31:5 + --> $DIR/integer_arithmetic.rs:33:5 | LL | i += 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:32:5 + --> $DIR/integer_arithmetic.rs:34:5 | LL | i -= 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:33:5 + --> $DIR/integer_arithmetic.rs:35:5 | LL | i *= 2; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:34:5 + --> $DIR/integer_arithmetic.rs:37:5 | -LL | i /= 2; +LL | i /= 0; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:35:5 + --> $DIR/integer_arithmetic.rs:38:11 | -LL | i %= 2; +LL | i /= -1; + | ^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:39:5 + | +LL | i /= var1; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:40:5 + | +LL | i /= var2; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:42:5 + | +LL | i %= 0; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:36:5 + --> $DIR/integer_arithmetic.rs:43:11 + | +LL | i %= -1; + | ^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:44:5 + | +LL | i %= var1; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:45:5 + | +LL | i %= var2; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:46:5 | LL | i <<= 3; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:37:5 + --> $DIR/integer_arithmetic.rs:47:5 | LL | i >>= 2; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:79:5 + --> $DIR/integer_arithmetic.rs:89:5 | LL | 3 + &1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:80:5 + --> $DIR/integer_arithmetic.rs:90:5 | LL | &3 + 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:81:5 + --> $DIR/integer_arithmetic.rs:91:5 | LL | &3 + &1; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:86:5 + --> $DIR/integer_arithmetic.rs:96:5 | LL | a + x | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:90:5 + --> $DIR/integer_arithmetic.rs:100:5 | LL | x + y | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:94:5 + --> $DIR/integer_arithmetic.rs:104:5 | LL | x + y | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:98:5 + --> $DIR/integer_arithmetic.rs:108:5 | LL | (&x + &y) | ^^^^^^^^^ -error: aborting due to 21 previous errors +error: aborting due to 29 previous errors From e3de544c22feb01437a060e88249652c103cb612 Mon Sep 17 00:00:00 2001 From: Christian Nielsen Date: Thu, 29 Oct 2020 15:49:42 +0100 Subject: [PATCH 0862/1110] Remove empty lines in doc comment Co-authored-by: Philipp Krones --- clippy_lints/src/len_zero.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index d0e7dea4a54..8e2f03d6e4e 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -85,7 +85,7 @@ declare_clippy_lint! { /// if s == "" { /// .. /// } - + /// /// if arr == [] { /// .. /// } @@ -95,7 +95,7 @@ declare_clippy_lint! { /// if s.is_empty() { /// .. /// } - + /// /// if arr.is_empty() { /// .. /// } From 38ec920fb0f7b39730317126a5286a5cc9c59d3b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 30 Oct 2020 00:24:29 +0900 Subject: [PATCH 0863/1110] Update CONTRIBUTING.md to describe `E-medium` in detail Co-authored-by: Philipp Krones --- CONTRIBUTING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index abb82c46935..a8e2123656e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,8 +64,9 @@ Usually the lint will end up to be a nested series of matches and ifs, [like so] But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. [`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] -first. They are mostly classified as [`E-medium`], since they might be somewhat involved code wise, -but not difficult per-se. +first. Sometimes they are only somewhat involved code wise, but not difficult per-se. +Note that [`E-medium`] issues may require some knowledge of Clippy internals or some +debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of From 230d9cbe36b809d45efb37092b102bc5d3b350d8 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:39:19 +0100 Subject: [PATCH 0864/1110] Update clippy_lints/src/ref_option_ref.rs Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index e8c60842e76..a914a77d48b 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { cx, REF_OPTION_REF, ty.span, - "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", + "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", "try", format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), Applicability::MaybeIncorrect, From 7b065dba84362ce90a7d9a920e193dc719198a9b Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:38:46 +0100 Subject: [PATCH 0865/1110] clippy: update reference file to match suggested change --- tests/ui/ref_option_ref.stderr | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 10f19661268..4e7fc800061 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,4 +1,4 @@ -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:10:23 | LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); @@ -6,61 +6,61 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); | = note: `-D clippy::ref-option-ref` implied by `-D warnings` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:12:18 | LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:14:25 | LL | type RefOptRefU32<'a> = &'a Option<&'a u32>; | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:15:25 | LL | type RefOptRef<'a, T> = &'a Option<&'a T>; | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:17:14 | LL | fn foo(data: &Option<&u32>) {} | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:19:23 | LL | fn bar(data: &u32) -> &Option<&u32> { | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:24:11 | LL | data: &'a Option<&'a u32>, | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:27:32 | LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:31:14 | LL | Variant2(&'a Option<&'a u32>), | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:40:14 | LL | type A = &'static Option<&'static Self>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:46:12 | LL | let x: &Option<&u32> = &None; From d780f61d7b3febdf0808dfef9abe80e95f2d46f8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 29 Oct 2020 19:08:54 +0100 Subject: [PATCH 0866/1110] add manual_ok_or / pr remarks --- clippy_lints/src/manual_ok_or.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index 38298eb813a..c99d2e35b94 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,4 +1,6 @@ -use crate::utils; +use crate::utils::{ + indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, PatKind, QPath}; @@ -49,18 +51,18 @@ impl LateLintPass<'_> for ManualOkOr { if args.len() == 3; let method_receiver = &args[0]; let ty = cx.typeck_results().expr_ty(method_receiver); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if is_type_diagnostic_item(cx, ty, sym!(option_type)); let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; - if utils::match_qpath(err_path, &utils::paths::RESULT_ERR); - if let Some(method_receiver_snippet) = utils::snippet_opt(cx, method_receiver.span); - if let Some(err_arg_snippet) = utils::snippet_opt(cx, err_arg.span); - if let Some(indent) = utils::indent_of(cx, scrutinee.span); + if match_qpath(err_path, &paths::RESULT_ERR); + if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span); + if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); + if let Some(indent) = indent_of(cx, scrutinee.span); then { let reindented_err_arg_snippet = - utils::reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); - utils::span_lint_and_sugg( + reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + span_lint_and_sugg( cx, MANUAL_OK_OR, scrutinee.span, @@ -80,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr { fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { if let ExprKind::Path(ref qpath) = map_expr.kind { - if utils::match_qpath(qpath, &utils::paths::RESULT_OK) { + if match_qpath(qpath, &paths::RESULT_OK) { return true; } } @@ -89,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir().body(body_id); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if utils::match_qpath(ok_path, &utils::paths::RESULT_OK); + if match_qpath(ok_path, &paths::RESULT_OK); if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind; if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res; then { param_id == ok_arg_path_id } else { false } From fa0a78b130d52d4504eefbd16689b140e05fce59 Mon Sep 17 00:00:00 2001 From: henil Date: Fri, 30 Oct 2020 17:11:44 +0530 Subject: [PATCH 0867/1110] removed lint for division/modulo for literal `0` --- clippy_lints/src/arithmetic.rs | 7 +------ tests/ui/integer_arithmetic.stderr | 14 +------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index b1cdbab6de9..9861d8cfc4e 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -90,12 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { match op.node { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { - hir::ExprKind::Lit(lit) => { - if let rustc_ast::ast::LitKind::Int(0, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_span = Some(expr.span); - } - }, + hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => { if let hir::ExprKind::Lit(lit) = &expr.kind { if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index 20356afc358..add3b6b90fa 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -75,12 +75,6 @@ error: integer arithmetic detected LL | i *= 2; | ^^^^^^ -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:37:5 - | -LL | i /= 0; - | ^^^^^^ - error: integer arithmetic detected --> $DIR/integer_arithmetic.rs:38:11 | @@ -99,12 +93,6 @@ error: integer arithmetic detected LL | i /= var2; | ^^^^^^^^^ -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:42:5 - | -LL | i %= 0; - | ^^^^^^ - error: integer arithmetic detected --> $DIR/integer_arithmetic.rs:43:11 | @@ -177,5 +165,5 @@ error: integer arithmetic detected LL | (&x + &y) | ^^^^^^^^^ -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors From 9f402c991b83810b95b7a85a926330ba06c56d37 Mon Sep 17 00:00:00 2001 From: Randall Mason Date: Fri, 30 Oct 2020 12:16:11 -0500 Subject: [PATCH 0868/1110] Clarify allow/warn/deny. Remove enable/disable. Disable and enable when not specifically explained were not clear to me as an English language speaker, but I was able to figure it out fairly easily due to the examples having A/W, which I assumed meant `allow` and `warn`. I removed both words to be sure it was clear as well as extending the note on what deny means. It now includes a statement on exactly what each word means. --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e1b3c84d691..36a816b9ee6 100644 --- a/README.md +++ b/README.md @@ -167,18 +167,22 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: * `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. -Note: `deny` produces errors instead of warnings. +Note: `allow` in this case means to "allow your code to have the lint without +warning". `deny` means "produce an error if your code has the lint". `warn` +means "produce a warning, but don't produce an error due to this lint". An +error causes clippy to exit with an error code, so is useful in scripts like +CI/CD. -If you do not want to include your lint levels in your code, you can globally enable/disable lints -by passing extra flags to Clippy during the run: +If you do not want to include your lint levels in your code, you can globally +enable/disable lints by passing extra flags to Clippy during the run: -To disable `lint_name`, run +To allow `lint_name`, run ```terminal cargo clippy -- -A clippy::lint_name ``` -And to enable `lint_name`, run +And to warn on `lint_name`, run ```terminal cargo clippy -- -W clippy::lint_name @@ -190,7 +194,7 @@ can run Clippy with warnings for all lints enabled: cargo clippy -- -W clippy::pedantic ``` -If you care only about a single lint, you can allow all others and then explicitly reenable +If you care only about a single lint, you can allow all others and then explicitly warn on the lint(s) you are interested in: ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... From 7c74d870b5978fb100f8947850d0da3f38dd5c1c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 13:01:34 -0500 Subject: [PATCH 0869/1110] Fix vec_box scope error Fixes #6236 --- clippy_lints/src/types.rs | 2 +- tests/ui/vec_box_sized.fixed | 14 ++++++++++++++ tests/ui/vec_box_sized.rs | 14 ++++++++++++++ tests/ui/vec_box_sized.stderr | 8 +++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 45f3bc3ea85..1bb81bbc9a3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -553,7 +553,7 @@ impl Types { hir_ty.span, "`Vec` is already on the heap, the boxing is unnecessary.", "try", - format!("Vec<{}>", ty_ty), + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index d0bee2460dd..4fa28b525c3 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -35,4 +35,18 @@ mod should_not_trigger { } } +mod inner_mod { + mod inner { + pub struct S; + } + + mod inner2 { + use super::inner::S; + + pub fn f() -> Vec { + vec![] + } + } +} + fn main() {} diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index 500a0ae263e..7dc735cd90b 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -35,4 +35,18 @@ mod should_not_trigger { } } +mod inner_mod { + mod inner { + pub struct S; + } + + mod inner2 { + use super::inner::S; + + pub fn f() -> Vec> { + vec![] + } + } +} + fn main() {} diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr index 29bf7069e8a..57e2f1fdf9a 100644 --- a/tests/ui/vec_box_sized.stderr +++ b/tests/ui/vec_box_sized.stderr @@ -18,5 +18,11 @@ error: `Vec` is already on the heap, the boxing is unnecessary. LL | struct B(Vec>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` -error: aborting due to 3 previous errors +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:46:23 + | +LL | pub fn f() -> Vec> { + | ^^^^^^^^^^^ help: try: `Vec` + +error: aborting due to 4 previous errors From c0d1002d9328104808253b5cb680d0cd0ab70c1a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 16:06:27 -0500 Subject: [PATCH 0870/1110] Fix unnecessary_lazy_eval suggestion applicability Fixes #6240 --- .../src/methods/unnecessary_lazy_eval.rs | 12 +++++++++- tests/ui/unnecessary_lazy_eval_unfixable.rs | 18 +++++++++++++++ .../ui/unnecessary_lazy_eval_unfixable.stderr | 22 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_lazy_eval_unfixable.rs create mode 100644 tests/ui/unnecessary_lazy_eval_unfixable.stderr diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 08b3eab9b7c..aec1b7e2e46 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -32,6 +32,16 @@ pub(super) fn lint<'tcx>( } else { "unnecessary closure used to substitute value for `Result::Err`" }; + let applicability = if body + .params + .iter() + .all(|param| matches!(param.pat.kind, hir::PatKind::Wild)) + { + Applicability::MachineApplicable + } else { + // replacing the lambda may break type inference + Applicability::MaybeIncorrect + }; span_lint_and_sugg( cx, @@ -45,7 +55,7 @@ pub(super) fn lint<'tcx>( simplify_using, snippet(cx, body_expr.span, ".."), ), - Applicability::MachineApplicable, + applicability, ); } } diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs new file mode 100644 index 00000000000..2e923bc97a2 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -0,0 +1,18 @@ +#![warn(clippy::unnecessary_lazy_evaluations)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +fn main() { + // fix will break type inference + let _ = Ok(1).unwrap_or_else(|()| 2); + mod e { + pub struct E; + } + let _ = Ok(1).unwrap_or_else(|e::E| 2); + let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); +} diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr new file mode 100644 index 00000000000..581d641cbf5 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -0,0 +1,22 @@ +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)` + | + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` + +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)` + +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)` + +error: aborting due to 3 previous errors + From 2350ee75b2cc9c3d8cfbbb8d950012678913b92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 13 Sep 2020 13:00:45 +0200 Subject: [PATCH 0871/1110] single_char_push_str lint using insert_str() on single-char literals and suggest insert() changelog: single_char_push_str: lint using string.insert_str() with single char literals and suggests string.insert() with a char Fixes #6026 --- clippy_lints/src/methods/mod.rs | 25 ++++++++++++++-- clippy_lints/src/utils/paths.rs | 1 + tests/ui/single_char_insert_str.fixed | 18 ++++++++++++ tests/ui/single_char_insert_str.rs | 18 ++++++++++++ tests/ui/single_char_insert_str.stderr | 40 ++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 tests/ui/single_char_insert_str.fixed create mode 100644 tests/ui/single_char_insert_str.rs create mode 100644 tests/ui/single_char_insert_str.stderr diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..86e7c57afae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1290,8 +1290,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using `push_str` with a single-character string literal, - /// and `push` with a `char` would work fine. + /// **What it does:** Warns when using `push_str` with a single-character string literal + /// where `push` with a `char` would work fine. /// /// **Why is this bad?** It's less clear that we are pushing a single character. /// @@ -1521,6 +1521,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { lint_single_char_push_string(cx, expr, args); + } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { + lint_single_char_insert_string(cx, expr, args); } } @@ -3255,6 +3257,25 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args } } +/// lint for length-1 `str`s as argument for `insert_str` +fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let pos_arg = snippet(cx, args[1].span, ".."); + let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `insert_str()` using a single-character string literal", + "consider using `insert` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd72fdd61fd..34d5c8d2eb7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -52,6 +52,7 @@ pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entr pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed new file mode 100644 index 00000000000..c3416720ec3 --- /dev/null +++ b/tests/ui/single_char_insert_str.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.insert(0, 'R'); + string.insert(1, '\''); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert(0, '\x52'); + string.insert(0, '\u{0052}'); + let x: usize = 2; + string.insert(x, 'a'); + const Y: usize = 1; + string.insert(Y, 'a'); +} diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs new file mode 100644 index 00000000000..2295669e81d --- /dev/null +++ b/tests/ui/single_char_insert_str.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.insert_str(0, "R"); + string.insert_str(1, "'"); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert_str(0, "\x52"); + string.insert_str(0, "\u{0052}"); + let x: usize = 2; + string.insert_str(x, r##"a"##); + const Y: usize = 1; + string.insert_str(Y, r##"a"##); +} diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr new file mode 100644 index 00000000000..65dc8a69d69 --- /dev/null +++ b/tests/ui/single_char_insert_str.stderr @@ -0,0 +1,40 @@ +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:6:5 + | +LL | string.insert_str(0, "R"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:7:5 + | +LL | string.insert_str(1, "'"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:12:5 + | +LL | string.insert_str(0, "/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:13:5 + | +LL | string.insert_str(0, "/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:15:5 + | +LL | string.insert_str(x, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:17:5 + | +LL | string.insert_str(Y, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` + +error: aborting due to 6 previous errors + From c6412aeebc432ab49d46fe1f45a0fb5322c805d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 14 Sep 2020 15:31:04 +0200 Subject: [PATCH 0872/1110] handle macros returning Strings in single_char_push_str and single_char_insert_str --- clippy_lints/src/methods/mod.rs | 6 ++++-- tests/ui/single_char_insert_str.fixed | 8 ++++++++ tests/ui/single_char_insert_str.rs | 8 ++++++++ tests/ui/single_char_insert_str.stderr | 20 +++++++++++++------- tests/ui/single_char_push_str.fixed | 8 ++++++++ tests/ui/single_char_push_str.rs | 8 ++++++++ tests/ui/single_char_push_str.stderr | 10 +++++----- 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 86e7c57afae..68a152270e0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3243,7 +3243,8 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability); let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, @@ -3261,7 +3262,8 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); let pos_arg = snippet(cx, args[1].span, ".."); let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed index c3416720ec3..fd43f913330 100644 --- a/tests/ui/single_char_insert_str.fixed +++ b/tests/ui/single_char_insert_str.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.insert(0, 'R'); @@ -15,4 +21,6 @@ fn main() { string.insert(x, 'a'); const Y: usize = 1; string.insert(Y, 'a'); + + get_string!().insert(1, '?'); } diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs index 2295669e81d..4278f7ef9fd 100644 --- a/tests/ui/single_char_insert_str.rs +++ b/tests/ui/single_char_insert_str.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.insert_str(0, "R"); @@ -15,4 +21,6 @@ fn main() { string.insert_str(x, r##"a"##); const Y: usize = 1; string.insert_str(Y, r##"a"##); + + get_string!().insert_str(1, "?"); } diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr index 65dc8a69d69..3d00c91a2ac 100644 --- a/tests/ui/single_char_insert_str.stderr +++ b/tests/ui/single_char_insert_str.stderr @@ -1,5 +1,5 @@ error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:6:5 + --> $DIR/single_char_insert_str.rs:12:5 | LL | string.insert_str(0, "R"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` @@ -7,34 +7,40 @@ LL | string.insert_str(0, "R"); = note: `-D clippy::single-char-push-str` implied by `-D warnings` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:7:5 + --> $DIR/single_char_insert_str.rs:13:5 | LL | string.insert_str(1, "'"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:12:5 + --> $DIR/single_char_insert_str.rs:18:5 | LL | string.insert_str(0, "/x52"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:13:5 + --> $DIR/single_char_insert_str.rs:19:5 | LL | string.insert_str(0, "/u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:15:5 + --> $DIR/single_char_insert_str.rs:21:5 | LL | string.insert_str(x, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:17:5 + --> $DIR/single_char_insert_str.rs:23:5 | LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` -error: aborting due to 6 previous errors +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:25:5 + | +LL | get_string!().insert_str(1, "?"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` + +error: aborting due to 7 previous errors diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index 0812c026a64..da742fe70e2 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.push('R'); @@ -12,4 +18,6 @@ fn main() { string.push('\x52'); string.push('\u{0052}'); string.push('a'); + + get_string!().push_str("ö"); } diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs index ab293bbe4ee..a8203e6503e 100644 --- a/tests/ui/single_char_push_str.rs +++ b/tests/ui/single_char_push_str.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.push_str("R"); @@ -12,4 +18,6 @@ fn main() { string.push_str("\x52"); string.push_str("\u{0052}"); string.push_str(r##"a"##); + + get_string!().push_str("ö"); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 0e9bdaa23e7..1f535589121 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -1,5 +1,5 @@ error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:6:5 + --> $DIR/single_char_push_str.rs:12:5 | LL | string.push_str("R"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` @@ -7,25 +7,25 @@ LL | string.push_str("R"); = note: `-D clippy::single-char-push-str` implied by `-D warnings` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:7:5 + --> $DIR/single_char_push_str.rs:13:5 | LL | string.push_str("'"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:12:5 + --> $DIR/single_char_push_str.rs:18:5 | LL | string.push_str("/x52"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:13:5 + --> $DIR/single_char_push_str.rs:19:5 | LL | string.push_str("/u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:14:5 + --> $DIR/single_char_push_str.rs:20:5 | LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` From c1eb8ceede6538590a39721c65ee2f943ceffe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 24 Sep 2020 16:36:29 +0200 Subject: [PATCH 0873/1110] get_hint_if_single_char_arg: fix bug where multi-char letters are not detected properly --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/single_char_pattern.fixed | 6 +++--- tests/ui/single_char_pattern.stderr | 20 +++++++++++++++++++- tests/ui/single_char_push_str.fixed | 2 +- tests/ui/single_char_push_str.stderr | 8 +++++++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 68a152270e0..5cc3c25e01d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3204,7 +3204,7 @@ fn get_hint_if_single_char_arg( if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; let string = r.as_str(); - if string.len() == 1; + if string.chars().count() == 1; then { let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 3871c4f2268..817a06199ff 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -18,9 +18,9 @@ fn main() { // // We may not want to suggest changing these anyway // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 - x.split("ß"); - x.split("ℝ"); - x.split("💣"); + x.split('ß'); + x.split('ℝ'); + x.split('💣'); // Can't use this lint for unicode code points which don't fit in a char x.split("❤️"); x.contains('x'); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index fe7211c53f8..ecaabd9155b 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -6,6 +6,24 @@ LL | x.split("x"); | = note: `-D clippy::single-char-pattern` implied by `-D warnings` +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:21:13 + | +LL | x.split("ß"); + | ^^^ help: try using a `char` instead: `'ß'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:22:13 + | +LL | x.split("ℝ"); + | ^^^ help: try using a `char` instead: `'ℝ'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:23:13 + | +LL | x.split("💣"); + | ^^^^ help: try using a `char` instead: `'💣'` + error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:26:16 | @@ -162,5 +180,5 @@ error: single-character string constant used as pattern LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` -error: aborting due to 27 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index da742fe70e2..3c550bee9a3 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -19,5 +19,5 @@ fn main() { string.push('\u{0052}'); string.push('a'); - get_string!().push_str("ö"); + get_string!().push('ö'); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 1f535589121..d6e6e635cc5 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -30,5 +30,11 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` -error: aborting due to 5 previous errors +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:22:5 + | +LL | get_string!().push_str("ö"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` + +error: aborting due to 6 previous errors From d958269fe5afb26ee5026be5bc0cea459593bccf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 8 Oct 2020 00:03:26 +0200 Subject: [PATCH 0874/1110] Rename single_char_push_str to single_char_add_str --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/methods/mod.rs | 16 ++-- src/lintlist/mod.rs | 12 +-- ...rt_str.fixed => single_char_add_str.fixed} | 19 ++++- ...r_insert_str.rs => single_char_add_str.rs} | 19 ++++- tests/ui/single_char_add_str.stderr | 82 +++++++++++++++++++ tests/ui/single_char_insert_str.stderr | 46 ----------- tests/ui/single_char_pattern.fixed | 6 -- tests/ui/single_char_pattern.rs | 6 -- tests/ui/single_char_pattern.stderr | 58 ++++++------- tests/ui/single_char_push_str.fixed | 23 ------ tests/ui/single_char_push_str.rs | 23 ------ tests/ui/single_char_push_str.stderr | 40 --------- 14 files changed, 166 insertions(+), 192 deletions(-) rename tests/ui/{single_char_insert_str.fixed => single_char_add_str.fixed} (58%) rename tests/ui/{single_char_insert_str.rs => single_char_add_str.rs} (58%) create mode 100644 tests/ui/single_char_add_str.stderr delete mode 100644 tests/ui/single_char_insert_str.stderr delete mode 100644 tests/ui/single_char_push_str.fixed delete mode 100644 tests/ui/single_char_push_str.rs delete mode 100644 tests/ui/single_char_push_str.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..27e861b0d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1939,8 +1939,8 @@ Released 2018-09-13 [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern -[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97358e06c63..f2056c72c07 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -713,8 +713,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, + &methods::SINGLE_CHAR_ADD_STR, &methods::SINGLE_CHAR_PATTERN, - &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -1438,8 +1438,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_ADD_STR), LintId::of(&methods::SINGLE_CHAR_PATTERN), - LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1631,7 +1631,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(&methods::SINGLE_CHAR_PUSH_STR), + LintId::of(&methods::SINGLE_CHAR_ADD_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5cc3c25e01d..89a2db2b76b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1290,8 +1290,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using `push_str` with a single-character string literal - /// where `push` with a `char` would work fine. + /// **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal + /// where `push`/`insert` with a `char` would work fine. /// /// **Why is this bad?** It's less clear that we are pushing a single character. /// @@ -1300,16 +1300,18 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let mut string = String::new(); + /// string.insert_str(0, "R"); /// string.push_str("R"); /// ``` /// Could be written as /// ```rust /// let mut string = String::new(); + /// string.insert(0, 'R'); /// string.push('R'); /// ``` - pub SINGLE_CHAR_PUSH_STR, + pub SINGLE_CHAR_ADD_STR, style, - "`push_str()` used with a single-character string literal as parameter" + "`push_str()` or `insert_str()` used with a single-character string literal as parameter" } declare_clippy_lint! { @@ -1390,7 +1392,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, @@ -3248,7 +3250,7 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, expr.span, "calling `push_str()` using a single-character string literal", "consider using `push` with a character literal", @@ -3268,7 +3270,7 @@ fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ar let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( cx, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, expr.span, "calling `insert_str()` using a single-character string literal", "consider using `insert` with a character literal", diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..b22bb074238 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2147,16 +2147,16 @@ vec![ module: "non_expressive_names", }, Lint { - name: "single_char_pattern", - group: "perf", - desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", + name: "single_char_add_str", + group: "style", + desc: "`push_str()` or `insert_str()` used with a single-character string literal as parameter", deprecation: None, module: "methods", }, Lint { - name: "single_char_push_str", - group: "style", - desc: "`push_str()` used with a single-character string literal as parameter", + name: "single_char_pattern", + group: "perf", + desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", deprecation: None, module: "methods", }, diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_add_str.fixed similarity index 58% rename from tests/ui/single_char_insert_str.fixed rename to tests/ui/single_char_add_str.fixed index fd43f913330..2feba4b8069 100644 --- a/tests/ui/single_char_insert_str.fixed +++ b/tests/ui/single_char_add_str.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::single_char_push_str)] +#![warn(clippy::single_char_add_str)] macro_rules! get_string { () => { @@ -8,6 +8,23 @@ macro_rules! get_string { } fn main() { + // `push_str` tests + + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); + + get_string!().push('ö'); + + // `insert_str` tests + let mut string = String::new(); string.insert(0, 'R'); string.insert(1, '\''); diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_add_str.rs similarity index 58% rename from tests/ui/single_char_insert_str.rs rename to tests/ui/single_char_add_str.rs index 4278f7ef9fd..681b3d10345 100644 --- a/tests/ui/single_char_insert_str.rs +++ b/tests/ui/single_char_add_str.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::single_char_push_str)] +#![warn(clippy::single_char_add_str)] macro_rules! get_string { () => { @@ -8,6 +8,23 @@ macro_rules! get_string { } fn main() { + // `push_str` tests + + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); + + get_string!().push_str("ö"); + + // `insert_str` tests + let mut string = String::new(); string.insert_str(0, "R"); string.insert_str(1, "'"); diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr new file mode 100644 index 00000000000..2b17279a564 --- /dev/null +++ b/tests/ui/single_char_add_str.stderr @@ -0,0 +1,82 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:14:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-add-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:15:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:20:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:21:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:22:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:24:5 + | +LL | get_string!().push_str("ö"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:29:5 + | +LL | string.insert_str(0, "R"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:30:5 + | +LL | string.insert_str(1, "'"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:35:5 + | +LL | string.insert_str(0, "/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:36:5 + | +LL | string.insert_str(0, "/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:38:5 + | +LL | string.insert_str(x, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:40:5 + | +LL | string.insert_str(Y, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:42:5 + | +LL | get_string!().insert_str(1, "?"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr deleted file mode 100644 index 3d00c91a2ac..00000000000 --- a/tests/ui/single_char_insert_str.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:12:5 - | -LL | string.insert_str(0, "R"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` - | - = note: `-D clippy::single-char-push-str` implied by `-D warnings` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:13:5 - | -LL | string.insert_str(1, "'"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:18:5 - | -LL | string.insert_str(0, "/x52"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:19:5 - | -LL | string.insert_str(0, "/u{0052}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:21:5 - | -LL | string.insert_str(x, r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:23:5 - | -LL | string.insert_str(Y, r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:25:5 - | -LL | get_string!().insert_str(1, "?"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` - -error: aborting due to 7 previous errors - diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 817a06199ff..d8b5f19e144 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -12,12 +12,6 @@ fn main() { let y = "x"; x.split(y); - // Not yet testing for multi-byte characters - // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` - // should have done this but produced an ICE - // - // We may not want to suggest changing these anyway - // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 x.split('ß'); x.split('ℝ'); x.split('💣'); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index 32afe339cd8..a7bc73e3756 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -12,12 +12,6 @@ fn main() { let y = "x"; x.split(y); - // Not yet testing for multi-byte characters - // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` - // should have done this but produced an ICE - // - // We may not want to suggest changing these anyway - // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 x.split("ß"); x.split("ℝ"); x.split("💣"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index ecaabd9155b..ee4e7e50efd 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -7,175 +7,175 @@ LL | x.split("x"); = note: `-D clippy::single-char-pattern` implied by `-D warnings` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:21:13 + --> $DIR/single_char_pattern.rs:15:13 | LL | x.split("ß"); | ^^^ help: try using a `char` instead: `'ß'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:22:13 + --> $DIR/single_char_pattern.rs:16:13 | LL | x.split("ℝ"); | ^^^ help: try using a `char` instead: `'ℝ'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:23:13 + --> $DIR/single_char_pattern.rs:17:13 | LL | x.split("💣"); | ^^^^ help: try using a `char` instead: `'💣'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:26:16 + --> $DIR/single_char_pattern.rs:20:16 | LL | x.contains("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:27:19 + --> $DIR/single_char_pattern.rs:21:19 | LL | x.starts_with("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:28:17 + --> $DIR/single_char_pattern.rs:22:17 | LL | x.ends_with("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:29:12 + --> $DIR/single_char_pattern.rs:23:12 | LL | x.find("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:30:13 + --> $DIR/single_char_pattern.rs:24:13 | LL | x.rfind("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:31:14 + --> $DIR/single_char_pattern.rs:25:14 | LL | x.rsplit("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:32:24 + --> $DIR/single_char_pattern.rs:26:24 | LL | x.split_terminator("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:33:25 + --> $DIR/single_char_pattern.rs:27:25 | LL | x.rsplit_terminator("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:34:17 + --> $DIR/single_char_pattern.rs:28:17 | LL | x.splitn(0, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:35:18 + --> $DIR/single_char_pattern.rs:29:18 | LL | x.rsplitn(0, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:36:15 + --> $DIR/single_char_pattern.rs:30:15 | LL | x.matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:37:16 + --> $DIR/single_char_pattern.rs:31:16 | LL | x.rmatches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:38:21 + --> $DIR/single_char_pattern.rs:32:21 | LL | x.match_indices("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:39:22 + --> $DIR/single_char_pattern.rs:33:22 | LL | x.rmatch_indices("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:40:26 + --> $DIR/single_char_pattern.rs:34:26 | LL | x.trim_start_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:41:24 + --> $DIR/single_char_pattern.rs:35:24 | LL | x.trim_end_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:43:13 + --> $DIR/single_char_pattern.rs:37:13 | LL | x.split("/n"); | ^^^^ help: try using a `char` instead: `'/n'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:44:13 + --> $DIR/single_char_pattern.rs:38:13 | LL | x.split("'"); | ^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:13 + --> $DIR/single_char_pattern.rs:39:13 | LL | x.split("/'"); | ^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:50:31 + --> $DIR/single_char_pattern.rs:44:31 | LL | x.replace(";", ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:51:19 + --> $DIR/single_char_pattern.rs:45:19 | LL | x.starts_with("/x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'/x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:58:13 + --> $DIR/single_char_pattern.rs:52:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:59:13 + --> $DIR/single_char_pattern.rs:53:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:60:13 + --> $DIR/single_char_pattern.rs:54:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:61:13 + --> $DIR/single_char_pattern.rs:55:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:62:13 + --> $DIR/single_char_pattern.rs:56:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed deleted file mode 100644 index 3c550bee9a3..00000000000 --- a/tests/ui/single_char_push_str.fixed +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.push('R'); - string.push('\''); - - string.push('u'); - string.push_str("st"); - string.push_str(""); - string.push('\x52'); - string.push('\u{0052}'); - string.push('a'); - - get_string!().push('ö'); -} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs deleted file mode 100644 index a8203e6503e..00000000000 --- a/tests/ui/single_char_push_str.rs +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.push_str("R"); - string.push_str("'"); - - string.push('u'); - string.push_str("st"); - string.push_str(""); - string.push_str("\x52"); - string.push_str("\u{0052}"); - string.push_str(r##"a"##); - - get_string!().push_str("ö"); -} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr deleted file mode 100644 index d6e6e635cc5..00000000000 --- a/tests/ui/single_char_push_str.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:12:5 - | -LL | string.push_str("R"); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` - | - = note: `-D clippy::single-char-push-str` implied by `-D warnings` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:13:5 - | -LL | string.push_str("'"); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:18:5 - | -LL | string.push_str("/x52"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:19:5 - | -LL | string.push_str("/u{0052}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:20:5 - | -LL | string.push_str(r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:22:5 - | -LL | get_string!().push_str("ö"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` - -error: aborting due to 6 previous errors - From f8ac1f99efd996bea2b05c83a915a830d69e1690 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 30 Oct 2020 23:47:22 +0100 Subject: [PATCH 0875/1110] Address suggestions in PR review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 1 + clippy_lints/src/methods/mod.rs | 2 +- tests/ui/single_char_add_str.fixed | 2 ++ tests/ui/single_char_add_str.rs | 2 ++ tests/ui/single_char_add_str.stderr | 14 +++++++++++++- 6 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e861b0d3e..be47eeaa073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ Current beta, release 2020-11-19 * [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) * [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044) * [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) -* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) ### Moves and Deprecations diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2056c72c07..9d9097002d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1958,6 +1958,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); + ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 89a2db2b76b..558c90249cc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3266,7 +3266,7 @@ fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ar if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet(cx, args[1].span, ".."); + let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( cx, diff --git a/tests/ui/single_char_add_str.fixed b/tests/ui/single_char_add_str.fixed index 2feba4b8069..63a6d37a9cc 100644 --- a/tests/ui/single_char_add_str.fixed +++ b/tests/ui/single_char_add_str.fixed @@ -38,6 +38,8 @@ fn main() { string.insert(x, 'a'); const Y: usize = 1; string.insert(Y, 'a'); + string.insert(Y, '"'); + string.insert(Y, '\''); get_string!().insert(1, '?'); } diff --git a/tests/ui/single_char_add_str.rs b/tests/ui/single_char_add_str.rs index 681b3d10345..a799ea7d885 100644 --- a/tests/ui/single_char_add_str.rs +++ b/tests/ui/single_char_add_str.rs @@ -38,6 +38,8 @@ fn main() { string.insert_str(x, r##"a"##); const Y: usize = 1; string.insert_str(Y, r##"a"##); + string.insert_str(Y, r##"""##); + string.insert_str(Y, r##"'"##); get_string!().insert_str(1, "?"); } diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr index 2b17279a564..55d91583ad0 100644 --- a/tests/ui/single_char_add_str.stderr +++ b/tests/ui/single_char_add_str.stderr @@ -72,11 +72,23 @@ error: calling `insert_str()` using a single-character string literal LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:41:5 + | +LL | string.insert_str(Y, r##"""##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')` + error: calling `insert_str()` using a single-character string literal --> $DIR/single_char_add_str.rs:42:5 | +LL | string.insert_str(Y, r##"'"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:44:5 + | LL | get_string!().insert_str(1, "?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors From 2eb248dd16f6727a2cdaa66f3f529a53bbcc54c7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 31 Oct 2020 09:34:01 +0100 Subject: [PATCH 0876/1110] Fix formatting --- clippy_lints/src/redundant_clone.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index ae3b0a03754..344ed02361d 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -518,7 +518,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { self.possible_borrower.add(borrowed.local, lhs); }, other => { - if ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty).is_continue() { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { return; } rvalue_locals(other, |rhs| { From edfa13da4a54e88913aebc382dc47022ccc4fc4a Mon Sep 17 00:00:00 2001 From: dp304 <34493835+dp304@users.noreply.github.com> Date: Sat, 31 Oct 2020 03:11:03 +0100 Subject: [PATCH 0877/1110] Fix typo in adding_lints.md --- doc/adding_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 2572833b8de..b1dacfc9c6d 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -311,7 +311,7 @@ Running our UI test should now produce output that contains the lint message. According to [the rustc-dev-guide], the text should be matter of fact and avoid capitalization and periods, unless multiple sentences are needed. When code or an identifier must appear in a message or label, it should be -surrounded with single acute accents \`. +surrounded with single grave accents \`. [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs From 7b203f3da6b590059c12bb01d0873b31be347928 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 31 Oct 2019 18:02:02 +0200 Subject: [PATCH 0878/1110] Implement field_reassign_with_default - Implement `field_reassign_with_default` as a `LateLintPass` - Avoid triggering `default_trait_access` on a span already linted by `field_reassigned_with_default` - Merge `default_trait_access` and `field_reassign_with_default` into `Default` - Co-authored-by: Eduardo Broto - Fixes #568 --- CHANGELOG.md | 1 + clippy_lints/src/default.rs | 304 ++++++++++++++++++++ clippy_lints/src/default_trait_access.rs | 62 ---- clippy_lints/src/lib.rs | 11 +- src/lintlist/mod.rs | 9 +- tests/ui/field_reassign_with_default.rs | 110 +++++++ tests/ui/field_reassign_with_default.stderr | 75 +++++ 7 files changed, 505 insertions(+), 67 deletions(-) create mode 100644 clippy_lints/src/default.rs delete mode 100644 clippy_lints/src/default_trait_access.rs create mode 100644 tests/ui/field_reassign_with_default.rs create mode 100644 tests/ui/field_reassign_with_default.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..36b54416a19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1714,6 +1714,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file [`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map [`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs new file mode 100644 index 00000000000..612c5355338 --- /dev/null +++ b/clippy_lints/src/default.rs @@ -0,0 +1,304 @@ +use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{span_lint_and_note, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Adt, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for literal calls to `Default::default()`. + /// + /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is + /// being gotten than the generic `Default`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let s: String = Default::default(); + /// + /// // Good + /// let s = String::default(); + /// ``` + pub DEFAULT_TRAIT_ACCESS, + pedantic, + "checks for literal calls to `Default::default()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for immediate reassignment of fields initialized + /// with Default::default(). + /// + /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). + /// + /// **Known problems:** Assignments to patterns that are of tuple type are not linted. + /// + /// **Example:** + /// Bad: + /// ``` + /// # #[derive(Default)] + /// # struct A { i: i32 } + /// let mut a: A = Default::default(); + /// a.i = 42; + /// ``` + /// Use instead: + /// ``` + /// # #[derive(Default)] + /// # struct A { i: i32 } + /// let a = A { + /// i: 42, + /// .. Default::default() + /// }; + /// ``` + pub FIELD_REASSIGN_WITH_DEFAULT, + style, + "binding initialized with Default should have its fields set in the initializer" +} + +#[derive(Default)] +pub struct Default { + // Spans linted by `field_reassign_with_default`. + reassigned_linted: FxHashSet, +} + +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); + +impl LateLintPass<'_> for Default { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Avoid cases already linted by `field_reassign_with_default` + if !self.reassigned_linted.contains(&expr.span); + if let ExprKind::Call(ref path, ..) = expr.kind; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; + then { + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind() { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); + } + } + } + } + + fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + // find all binding statements like `let mut _ = T::default()` where `T::default()` is the + // `default` method of the `Default` trait, and store statement index in current block being + // checked and the name of the bound variable + let binding_statements_using_default = enumerate_bindings_using_default(cx, block); + + // start from the `let mut _ = _::default();` and look at all the following + // statements, see if they re-assign the fields of the binding + for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { + // the last statement of a block cannot trigger the lint + if stmt_idx == block.stmts.len() - 1 { + break; + } + + // find all "later statement"'s where the fields of the binding set as + // Default::default() get reassigned, unless the reassignment refers to the original binding + let mut first_assign = None; + let mut assigned_fields = Vec::new(); + let mut cancel_lint = false; + for consecutive_statement in &block.stmts[stmt_idx + 1..] { + // interrupt if the statement is a let binding (`Local`) that shadows the original + // binding + if stmt_shadows_binding(consecutive_statement, binding_name) { + break; + } + // find out if and which field was set by this `consecutive_statement` + else if let Some((field_ident, assign_rhs)) = + field_reassigned_by_stmt(consecutive_statement, binding_name) + { + // interrupt and cancel lint if assign_rhs references the original binding + if contains_name(binding_name, assign_rhs) { + cancel_lint = true; + break; + } + + // if the field was previously assigned, replace the assignment, otherwise insert the assignment + if let Some(prev) = assigned_fields + .iter_mut() + .find(|(field_name, _)| field_name == &field_ident.name) + { + *prev = (field_ident.name, assign_rhs); + } else { + assigned_fields.push((field_ident.name, assign_rhs)); + } + + // also set first instance of error for help message + if first_assign.is_none() { + first_assign = Some(consecutive_statement); + } + } + // interrupt also if no field was assigned, since we only want to look at consecutive statements + else { + break; + } + } + + // if there are incorrectly assigned fields, do a span_lint_and_note to suggest + // construction using `Ty { fields, ..Default::default() }` + if !assigned_fields.is_empty() && !cancel_lint { + // take the original assignment as span + let stmt = &block.stmts[stmt_idx]; + + if let StmtKind::Local(preceding_local) = &stmt.kind { + // filter out fields like `= Default::default()`, because the FRU already covers them + let assigned_fields = assigned_fields + .into_iter() + .filter(|(_, rhs)| !is_expr_default(rhs, cx)) + .collect::)>>(); + + // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. + let ext_with_default = !fields_of_type(binding_type) + .iter() + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + + let field_list = assigned_fields + .into_iter() + .map(|(field, rhs)| { + // extract and store the assigned value for help message + let value_snippet = snippet(cx, rhs.span, ".."); + format!("{}: {}", field, value_snippet) + }) + .collect::>() + .join(", "); + + let sugg = if ext_with_default { + if field_list.is_empty() { + format!("{}::default()", binding_type) + } else { + format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + } + } else { + format!("{} {{ {} }}", binding_type, field_list) + }; + + // span lint once per statement that binds default + span_lint_and_note( + cx, + FIELD_REASSIGN_WITH_DEFAULT, + first_assign.unwrap().span, + "field assignment outside of initializer for an instance created with Default::default()", + Some(preceding_local.span), + &format!( + "consider initializing the variable with `{}` and removing relevant reassignments", + sugg + ), + ); + self.reassigned_linted.insert(span); + } + } + } + } +} + +/// Checks if the given expression is the `default` method belonging to the `Default` trait. +fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { + if_chain! { + if let ExprKind::Call(ref fn_expr, _) = &expr.kind; + if let ExprKind::Path(qpath) = &fn_expr.kind; + if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id); + then { + // right hand side of assignment is `Default::default` + match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD) + } else { + false + } + } +} + +/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except +/// for when the pattern type is a tuple. +fn enumerate_bindings_using_default<'tcx>( + cx: &LateContext<'tcx>, + block: &Block<'tcx>, +) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { + block + .stmts + .iter() + .enumerate() + .filter_map(|(idx, stmt)| { + if_chain! { + // only take `let ...` statements + if let StmtKind::Local(ref local) = stmt.kind; + // only take bindings to identifiers + if let PatKind::Binding(_, _, ident, _) = local.pat.kind; + // that are not tuples + let ty = cx.typeck_results().pat_ty(local.pat); + if !matches!(ty.kind(), ty::Tuple(_)); + // only when assigning `... = Default::default()` + if let Some(ref expr) = local.init; + if is_expr_default(expr, cx); + then { + Some((idx, ident.name, ty, expr.span)) + } else { + None + } + } + }) + .collect() +} + +fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool { + if let StmtKind::Local(local) = &this.kind { + if let PatKind::Binding(_, _, ident, _) = local.pat.kind { + return ident.name == shadowed; + } + } + false +} + +/// Returns the reassigned field and the assigning expression (right-hand side of assign). +fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { + if_chain! { + // only take assignments + if let StmtKind::Semi(ref later_expr) = this.kind; + if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; + // only take assignments to fields where the left-hand side field is a field of + // the same binding as the previous statement + if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; + if let ExprKind::Path(ref qpath) = binding.kind; + if let QPath::Resolved(_, path) = qpath; + if let Some(second_binding_name) = path.segments.last(); + if second_binding_name.ident.name == binding_name; + then { + Some((field_ident, assign_rhs)) + } else { + None + } + } +} + +/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. +fn fields_of_type(ty: Ty<'_>) -> Vec { + if let Adt(adt, _) = ty.kind() { + if adt.is_struct() { + let variant = &adt.non_enum_variant(); + return variant.fields.iter().map(|f| f.ident).collect(); + } + } + vec![] +} diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs deleted file mode 100644 index 3048436d9a7..00000000000 --- a/clippy_lints/src/default_trait_access.rs +++ /dev/null @@ -1,62 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg}; - -declare_clippy_lint! { - /// **What it does:** Checks for literal calls to `Default::default()`. - /// - /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is - /// being gotten than the generic `Default`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // Bad - /// let s: String = Default::default(); - /// - /// // Good - /// let s = String::default(); - /// ``` - pub DEFAULT_TRAIT_ACCESS, - pedantic, - "checks for literal calls to `Default::default()`" -} - -declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]); - -impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(ref path, ..) = expr.kind; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); - // Detect and ignore ::default() because these calls do explicitly name the type. - if let QPath::Resolved(None, _path) = qpath; - then { - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind() { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - } - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..bb94b3f6cbe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -176,7 +176,7 @@ mod copies; mod copy_iterator; mod create_dir; mod dbg_macro; -mod default_trait_access; +mod default; mod dereference; mod derive; mod disallowed_method; @@ -537,7 +537,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, - &default_trait_access::DEFAULT_TRAIT_ACCESS, + &default::DEFAULT_TRAIT_ACCESS, + &default::FIELD_REASSIGN_WITH_DEFAULT, &dereference::EXPLICIT_DEREF_METHODS, &derive::DERIVE_HASH_XOR_EQ, &derive::DERIVE_ORD_XOR_PARTIAL_ORD, @@ -1049,7 +1050,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box duration_subsec::DurationSubsec); - store.register_late_pass(|| box default_trait_access::DefaultTraitAccess); store.register_late_pass(|| box indexing_slicing::IndexingSlicing); store.register_late_pass(|| box non_copy_const::NonCopyConst); store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); @@ -1100,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box exit::Exit); @@ -1212,7 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), - LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), + LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), @@ -1321,6 +1322,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), + LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), @@ -1581,6 +1583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..a3645a65182 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -359,7 +359,7 @@ vec![ group: "pedantic", desc: "checks for literal calls to `Default::default()`", deprecation: None, - module: "default_trait_access", + module: "default", }, Lint { name: "deprecated_cfg_attr", @@ -627,6 +627,13 @@ vec![ deprecation: None, module: "fallible_impl_from", }, + Lint { + name: "field_reassign_with_default", + group: "style", + desc: "binding initialized with Default should have its fields set in the initializer", + deprecation: None, + module: "default", + }, Lint { name: "filetype_is_file", group: "restriction", diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs new file mode 100644 index 00000000000..79a30c22f95 --- /dev/null +++ b/tests/ui/field_reassign_with_default.rs @@ -0,0 +1,110 @@ +#![warn(clippy::field_reassign_with_default)] + +#[derive(Default)] +struct A { + i: i32, + j: i64, +} + +struct B { + i: i32, + j: i64, +} + +/// Implements .next() that returns a different number each time. +struct SideEffect(i32); + +impl SideEffect { + fn new() -> SideEffect { + SideEffect(0) + } + fn next(&mut self) -> i32 { + self.0 += 1; + self.0 + } +} + +fn main() { + // wrong, produces first error in stderr + let mut a: A = Default::default(); + a.i = 42; + + // right + let mut a: A = Default::default(); + + // right + let a = A { + i: 42, + ..Default::default() + }; + + // right + let mut a: A = Default::default(); + if a.i == 0 { + a.j = 12; + } + + // right + let mut a: A = Default::default(); + let b = 5; + + // right + let mut b = 32; + let mut a: A = Default::default(); + b = 2; + + // right + let b: B = B { i: 42, j: 24 }; + + // right + let mut b: B = B { i: 42, j: 24 }; + b.i = 52; + + // right + let mut b = B { i: 15, j: 16 }; + let mut a: A = Default::default(); + b.i = 2; + + // wrong, produces second error in stderr + let mut a: A = Default::default(); + a.j = 43; + a.i = 42; + + // wrong, produces third error in stderr + let mut a: A = Default::default(); + a.i = 42; + a.j = 43; + a.j = 44; + + // wrong, produces fourth error in stderr + let mut a = A::default(); + a.i = 42; + + // wrong, but does not produce an error in stderr, because we can't produce a correct kind of + // suggestion with current implementation + let mut c: (i32, i32) = Default::default(); + c.0 = 42; + c.1 = 21; + + // wrong, produces the fifth error in stderr + let mut a: A = Default::default(); + a.i = Default::default(); + + // wrong, produces the sixth error in stderr + let mut a: A = Default::default(); + a.i = Default::default(); + a.j = 45; + + // right, because an assignment refers to another field + let mut x = A::default(); + x.i = 42; + x.j = 21 + x.i as i64; + + // right, we bail out if there's a reassignment to the same variable, since there is a risk of + // side-effects affecting the outcome + let mut x = A::default(); + let mut side_effect = SideEffect::new(); + x.i = side_effect.next(); + x.j = 2; + x.i = side_effect.next(); +} diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr new file mode 100644 index 00000000000..c788ebae552 --- /dev/null +++ b/tests/ui/field_reassign_with_default.stderr @@ -0,0 +1,75 @@ +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:30:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | + = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` +note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:29:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:70:5 + | +LL | a.j = 43; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:69:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:75:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:74:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:81:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:80:5 + | +LL | let mut a = A::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:91:5 + | +LL | a.i = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `A::default()` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:90:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:95:5 + | +LL | a.i = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:94:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + From 158bf9aa443c5ea93ff4b3def8c7220632e45442 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 31 Oct 2020 19:56:51 +0100 Subject: [PATCH 0879/1110] Fix incorrect suggestion for macro expansion in `deref_addrof` lint --- clippy_lints/src/reference.rs | 8 +++++++- tests/ui/deref_addrof.fixed | 15 ++++++++++++++- tests/ui/deref_addrof.rs | 15 ++++++++++++++- tests/ui/deref_addrof.stderr | 13 ++++++++++++- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 3fda00403c6..f1555ab3ec1 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -46,13 +46,19 @@ impl EarlyLintPass for DerefAddrOf { if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; + let sugg = if e.span.from_expansion() { + let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); + snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + } else { + snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() + }; span_lint_and_sugg( cx, DEREF_ADDROF, e.span, "immediately dereferencing a reference", "try this", - format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)), + sugg, applicability, ); } diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 9e5b51d6d5e..8cdec4201fe 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,4 +1,5 @@ // run-rustfix +#![warn(clippy::deref_addrof)] fn get_number() -> usize { 10 @@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize { #[allow(clippy::many_single_char_names, clippy::double_parens)] #[allow(unused_variables, unused_parens)] -#[warn(clippy::deref_addrof)] fn main() { let a = 10; let aref = &a; @@ -37,3 +37,16 @@ fn main() { let b = *aref; } + +macro_rules! m { + ($visitor: expr) => { + $visitor + }; +} + +pub struct S; +impl S { + pub fn f(&self) -> &Self { + m!(self) + } +} diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 5641a73cbc1..6433e63ca59 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,4 +1,5 @@ // run-rustfix +#![warn(clippy::deref_addrof)] fn get_number() -> usize { 10 @@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize { #[allow(clippy::many_single_char_names, clippy::double_parens)] #[allow(unused_variables, unused_parens)] -#[warn(clippy::deref_addrof)] fn main() { let a = 10; let aref = &a; @@ -37,3 +37,16 @@ fn main() { let b = **&aref; } + +macro_rules! m { + ($visitor: expr) => { + *&$visitor + }; +} + +pub struct S; +impl S { + pub fn f(&self) -> &Self { + m!(self) + } +} diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index bc51719e8a7..1fe49ad49cd 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -48,5 +48,16 @@ error: immediately dereferencing a reference LL | let b = **&aref; | ^^^^^^ help: try this: `aref` -error: aborting due to 8 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:43:9 + | +LL | *&$visitor + | ^^^^^^^^^^ help: try this: `$visitor` +... +LL | m!(self) + | -------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 9 previous errors From 343bdb33647627a6a001be039a107e7ef3362707 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 17:56:54 +0100 Subject: [PATCH 0880/1110] Give better suggestion by working on span on `deref_addrof` lint --- clippy_lints/src/reference.rs | 36 ++++++++++++++++++++++++++++++----- tests/ui/deref_addrof.fixed | 10 ++++++++++ tests/ui/deref_addrof.rs | 12 +++++++++++- tests/ui/deref_addrof.stderr | 17 ++++++++++++++--- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index f1555ab3ec1..8646d616735 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,9 +1,11 @@ -use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp}; +use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::BytePos; +// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -42,13 +44,37 @@ impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; - if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind; + if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); - snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + if let Ok(macro_source) = cx.sess.source_map().span_to_snippet(e.span) { + // Remove leading whitespace from the given span + // e.g: ` $visitor` turns into `$visitor` + let trim_leading_whitespaces = |span| { + if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()).map(|pos| { + span.lo() + BytePos(pos as u32) + }) + }) { + e.span.with_lo(start_no_whitespace) + } else { + span + } + }; + + let rpos = if *mutability == Mutability::Mut { + macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() + } else { + macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + }; + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + } else { + snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + } } else { snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() }; diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 8cdec4201fe..689e9d55223 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -44,9 +44,19 @@ macro_rules! m { }; } +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + $visitor + }; +} + pub struct S; impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 6433e63ca59..57effce5d12 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -40,7 +40,14 @@ fn main() { macro_rules! m { ($visitor: expr) => { - *&$visitor + *& $visitor + }; +} + +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + *& mut $visitor }; } @@ -49,4 +56,7 @@ impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 1fe49ad49cd..52c1f7d1da8 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -51,13 +51,24 @@ LL | let b = **&aref; error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:43:9 | -LL | *&$visitor - | ^^^^^^^^^^ help: try this: `$visitor` +LL | *& $visitor + | ^^^^^^^^^^^ help: try this: `$visitor` ... LL | m!(self) | -------- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:50:9 + | +LL | *& mut $visitor + | ^^^^^^^^^^^^^^^ help: try this: `$visitor` +... +LL | m_mut!(self) + | ------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors From ce98468158318a47f6bab3707d348e116451ad39 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 17:59:47 +0100 Subject: [PATCH 0881/1110] Fix incorrect suggestion when from expansion in `try_err` lint --- clippy_lints/src/try_err.rs | 10 ++++++++-- tests/ui/try_err.fixed | 16 ++++++++++++++++ tests/ui/try_err.rs | 16 ++++++++++++++++ tests/ui/try_err.stderr | 21 ++++++++++++++++----- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 3e747ec4ad9..e6d8e7521a8 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg, }; use if_chain::if_chain; @@ -92,9 +92,13 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let origin_snippet = if err_arg.span.from_expansion() { + // println!("\n\n{:?}", in_macro(expr.span)); + // println!("{:#?}", snippet(cx, err_arg.span, "_")); + let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { + // println!("from expansion"); snippet_with_macro_callsite(cx, err_arg.span, "_") } else { + // println!("just a snippet"); snippet(cx, err_arg.span, "_") }; let suggestion = if err_ty == expr_err_ty { @@ -102,6 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; + // println!("origin_snippet: {:#?}", origin_snippet); + // println!("suggestion: {:#?}", suggestion); span_lint_and_sugg( cx, diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 9e77dcd8731..053dd45f23e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -78,12 +78,28 @@ fn nested_error() -> Result { Ok(1) } +// Bad suggestion when in macro (see #6242) +macro_rules! try_validation { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => return Err(1), + } + }}; +} + +fn calling_macro() -> Result { + try_validation!(Ok::<_, i32>(5)); + Ok(5) +} + fn main() { basic_test().unwrap(); into_test().unwrap(); negative_test().unwrap(); closure_matches_test().unwrap(); closure_into_test().unwrap(); + calling_macro().unwrap(); // We don't want to lint in external macros try_err!(); diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 41bcb0a189e..215ca6a07e6 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -78,12 +78,28 @@ fn nested_error() -> Result { Ok(1) } +// Bad suggestion when in macro (see #6242) +macro_rules! try_validation { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => Err(1)?, + } + }}; +} + +fn calling_macro() -> Result { + try_validation!(Ok::<_, i32>(5)); + Ok(5) +} + fn main() { basic_test().unwrap(); into_test().unwrap(); negative_test().unwrap(); closure_matches_test().unwrap(); closure_into_test().unwrap(); + calling_macro().unwrap(); // We don't want to lint in external macros try_err!(); diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 3f1cbc17e72..443c8d08472 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -29,28 +29,39 @@ LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:106:9 + --> $DIR/try_err.rs:86:23 + | +LL | Err(_) => Err(1)?, + | ^^^^^^^ help: try this: `return Err(1)` +... +LL | try_validation!(Ok::<_, i32>(5)); + | --------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:122:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:113:9 + --> $DIR/try_err.rs:129:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:115:9 + --> $DIR/try_err.rs:131:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:123:9 + --> $DIR/try_err.rs:139:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors From 22cc77a232a0ec73475ff54b5116adf9defc8667 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 10:32:55 -0600 Subject: [PATCH 0882/1110] Use const rustc sym where possible --- clippy_lints/src/attrs.rs | 22 ++++---- clippy_lints/src/booleans.rs | 5 +- clippy_lints/src/bytecount.rs | 3 +- clippy_lints/src/cognitive_complexity.rs | 6 +- clippy_lints/src/doc.rs | 10 ++-- clippy_lints/src/explicit_write.rs | 3 +- clippy_lints/src/fallible_impl_from.rs | 8 +-- clippy_lints/src/format.rs | 13 +++-- clippy_lints/src/functions.rs | 3 +- clippy_lints/src/get_last_with_len.rs | 3 +- clippy_lints/src/if_let_some_result.rs | 3 +- clippy_lints/src/inherent_to_string.rs | 3 +- clippy_lints/src/inline_fn_without_body.rs | 4 +- clippy_lints/src/loops.rs | 22 ++++---- clippy_lints/src/manual_non_exhaustive.rs | 10 ++-- clippy_lints/src/manual_unwrap_or.rs | 5 +- clippy_lints/src/map_clone.rs | 4 +- clippy_lints/src/map_identity.rs | 5 +- clippy_lints/src/map_unit_fn.rs | 5 +- clippy_lints/src/match_on_vec_items.rs | 3 +- clippy_lints/src/matches.rs | 9 +-- .../src/methods/inefficient_to_string.rs | 3 +- clippy_lints/src/methods/mod.rs | 56 +++++++++---------- .../src/methods/option_map_unwrap_or.rs | 4 +- .../src/methods/unnecessary_lazy_eval.rs | 5 +- clippy_lints/src/missing_doc.rs | 7 ++- clippy_lints/src/missing_inline.rs | 3 +- clippy_lints/src/needless_borrow.rs | 3 +- clippy_lints/src/needless_pass_by_value.rs | 10 ++-- clippy_lints/src/new_without_default.rs | 3 +- clippy_lints/src/non_expressive_names.rs | 3 +- clippy_lints/src/option_if_let_else.rs | 3 +- clippy_lints/src/panic_in_result_fn.rs | 4 +- clippy_lints/src/partialeq_ne_impl.rs | 3 +- clippy_lints/src/pass_by_ref_or_value.rs | 6 +- clippy_lints/src/ptr.rs | 6 +- clippy_lints/src/ptr_offset_with_cast.rs | 3 +- clippy_lints/src/question_mark.rs | 3 +- clippy_lints/src/ranges.rs | 3 +- clippy_lints/src/redundant_clone.rs | 3 +- clippy_lints/src/repeat_once.rs | 3 +- clippy_lints/src/returns.rs | 3 +- clippy_lints/src/strings.rs | 3 +- clippy_lints/src/swap.rs | 3 +- clippy_lints/src/try_err.rs | 9 +-- clippy_lints/src/types.rs | 10 ++-- clippy_lints/src/unnecessary_sort_by.rs | 3 +- clippy_lints/src/unwrap.rs | 9 +-- clippy_lints/src/unwrap_in_result.rs | 14 ++--- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/mod.rs | 3 +- clippy_lints/src/write.rs | 5 +- 52 files changed, 192 insertions(+), 158 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index f6eadbdef0b..a004abb58b8 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_span::symbol::{Symbol, SymbolStr}; use semver::Version; @@ -286,14 +287,14 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { }, _ => {}, } - if items.is_empty() || !attr.has_name(sym!(deprecated)) { + if items.is_empty() || !attr.has_name(sym::deprecated) { return; } for item in items { if_chain! { if let NestedMetaItem::MetaItem(mi) = &item; if let MetaItemKind::NameValue(lit) = &mi.kind; - if mi.has_name(sym!(since)); + if mi.has_name(sym::since); then { check_semver(cx, item.span(), lit); } @@ -309,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } match item.kind { ItemKind::ExternCrate(..) | ItemKind::Use(..) => { - let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym!(macro_use))); + let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym::macro_use)); for attr in item.attrs { if in_external_macro(cx.sess(), attr.span) { @@ -326,7 +327,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { match item.kind { ItemKind::Use(..) => { if is_word(lint, sym!(unused_imports)) - || is_word(lint, sym!(deprecated)) + || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) || extract_clippy_lint(lint) @@ -411,8 +412,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMet let lint_store = cx.lints(); for lint in items { if let Some(lint_name) = extract_clippy_lint(lint) { - if let CheckLintNameResult::Tool(Err((None, _))) = - lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) + if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym::clippy)) { span_lint_and_then( cx, @@ -529,10 +529,10 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut for attr in attrs { if let Some(values) = attr.meta_item_list() { - if values.len() != 1 || !attr.has_name(sym!(inline)) { + if values.len() != 1 || !attr.has_name(sym::inline) { continue; } - if is_word(&values[0], sym!(always)) { + if is_word(&values[0], sym::always) { span_lint( cx, INLINE_ALWAYS, @@ -623,12 +623,12 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { if_chain! { // check cfg_attr - if attr.has_name(sym!(cfg_attr)); + if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); if items.len() == 2; // check for `rustfmt` if let Some(feature_item) = items[0].meta_item(); - if feature_item.has_name(sym!(rustfmt)); + if feature_item.has_name(sym::rustfmt); // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); if skip_item.has_name(sym!(rustfmt_skip)) || @@ -690,7 +690,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } if_chain! { - if attr.has_name(sym!(cfg)); + if attr.has_name(sym::cfg); if let Some(list) = attr.meta_item_list(); let mismatched = find_mismatched_target_os(&list); if !mismatched.is_empty(); diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 280a2c7fe67..90bb0bd555f 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for boolean expressions that can be written more @@ -253,8 +254,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { }, ExprKind::MethodCall(path, _, args, _) if args.len() == 1 => { let type_of_receiver = cx.typeck_results().expr_ty(&args[0]); - if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type)) - && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type)) + if !is_type_diagnostic_item(cx, type_of_receiver, sym::option_type) + && !is_type_diagnostic_item(cx, type_of_receiver, sym::result_type) { return None; } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index d7d02ebf985..38a0e27c4cf 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use rustc_span::Symbol; declare_clippy_lint! { @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = filter_args[0].kind { let p = path.ident.name; - if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 { + if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { &args[0] } else { &filter_args[0] diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 14ef8c319ef..b1bc2ec29e1 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::BytePos; +use rustc_span::{sym, BytePos}; use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack}; @@ -61,7 +61,7 @@ impl CognitiveComplexity { helper.visit_expr(expr); let CCHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); - let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) { + let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) { returns } else { #[allow(clippy::integer_division)] @@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { hir_id: HirId, ) { let def_id = cx.tcx.hir().local_def_id(hir_id); - if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) { + if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { self.check(cx, kind, decl, body, span); } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 07f604cf714..edecba57e44 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -15,7 +15,7 @@ use rustc_parse::maybe_new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; -use rustc_span::{FileName, Pos}; +use rustc_span::{sym, FileName, Pos}; use std::io; use std::ops::Range; use url::Url; @@ -237,7 +237,7 @@ fn lint_for_missing_headers<'tcx>( ); } if !headers.errors { - if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { span_lint( cx, MISSING_ERRORS_DOC, @@ -255,7 +255,7 @@ fn lint_for_missing_headers<'tcx>( if let ty::Opaque(_, subs) = ret_ty.kind(); if let Some(gen) = subs.types().next(); if let ty::Generator(_, subs, _) = gen.kind(); - if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type)); + if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::result_type); then { span_lint( cx, @@ -333,7 +333,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span); spans.extend_from_slice(¤t_spans); doc.push_str(&comment); - } else if attr.has_name(sym!(doc)) { + } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check return DocHeaders { @@ -479,7 +479,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) => return false, // We found a main function ... - ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { let is_async = matches!(sig.header.asyncness, Async::Yes{..}); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 0240e80d814..f8038d06e50 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be @@ -33,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { if_chain! { // match call to unwrap if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind; - if unwrap_fun.ident.name == sym!(unwrap); + if unwrap_fun.ident.name == sym::unwrap; // match call to write_fmt if !unwrap_args.is_empty(); if let ExprKind::MethodCall(ref write_fun, _, write_args, _) = diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index a9e05fddbe7..fe817fe94f2 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()` @@ -95,8 +95,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); - if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) - || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type) { self.result.push(expr.span); } @@ -113,7 +113,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h for impl_item in impl_items { if_chain! { - if impl_item.ident.name == sym!(from); + if impl_item.ident.name == sym::from; if let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind; then { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 26da058598e..8e41e0e34da 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -10,6 +10,7 @@ use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for the use of `format!("string literal with no @@ -91,7 +92,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if pats.len() == 1; then { let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); - if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) { return None; } if let ExprKind::Lit(ref lit) = format_args.kind { @@ -186,15 +187,15 @@ fn check_unformatted(expr: &Expr<'_>) -> bool { if exprs.len() == 1; // struct `core::fmt::rt::v1::Argument` if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format)); + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); // struct `core::fmt::rt::v1::FormatSpec` if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind; - if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision)); + if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision); if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; - if last_path_segment(precision_path).ident.name == sym!(Implied); - if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width)); + if last_path_segment(precision_path).ident.name == sym::Implied; + if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width); if let ExprKind::Path(ref width_qpath) = width_field.expr.kind; - if last_path_segment(width_qpath).ident.name == sym!(Implied); + if last_path_segment(width_qpath).ident.name == sym::Implied; then { return true; } diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 9c0efef95de..8b58d1f2601 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_target::spec::abi::Abi; use rustc_typeck::hir_ty_to_ty; @@ -473,7 +474,7 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span if !in_external_macro(cx.sess(), item_span); if let hir::FnRetTy::Return(ref ty) = decl.output; if let hir::TyKind::Path(ref qpath) = ty.kind; - if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type)); + if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type); if let Some(ref args) = last_path_segment(qpath).args; if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; if let hir::TyKind::Tup(t) = err_ty.kind; diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index 48ebcf5ebcd..cdd8a42e7cd 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of @@ -55,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { // Argument 0 (the struct we're calling the method on) is a vector if let Some(struct_calling_on) = args.get(0); let struct_ty = cx.typeck_results().expr_ty(struct_calling_on); - if is_type_diagnostic_item(cx, struct_ty, sym!(vec_type)); + if is_type_diagnostic_item(cx, struct_ty, sym::vec_type); // Argument to "get" is a subtraction if let Some(get_index_arg) = args.get(1); diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 28b20cdeac3..e0a1f4c5ca4 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -4,6 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:*** Checks for unnecessary `ok()` in if let. @@ -45,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym!(result_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; then { diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 0877b44d901..b723d06a688 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -2,6 +2,7 @@ use if_chain::if_chain; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use crate::utils::{ get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, @@ -107,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if decl.inputs.len() == 1; // Check if return type is String - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::string_type); // Filters instances of to_string which are required by a trait if trait_ref_of_method(cx, impl_item.hir_id).is_none(); diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 4b605fdb366..d1c3fdc7146 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// **What it does:** Checks for `#[inline]` on trait methods without bodies @@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { for attr in attrs { - if !attr.has_name(sym!(inline)) { + if !attr.has_name(sym::inline) { continue; } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c3f75f283f4..c7d3f4e9b08 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -619,9 +619,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } let lhs_constructor = last_path_segment(qpath); - if method_path.ident.name == sym!(next) + if method_path.ident.name == sym::next && match_trait_method(cx, match_expr, &paths::ITERATOR) - && lhs_constructor.ident.name == sym!(Some) + && lhs_constructor.ident.name == sym::Some && (pat_args.is_empty() || !is_refutable(cx, &pat_args[0]) && !is_used_inside(cx, iter_expr, &arms[0].body) @@ -985,13 +985,13 @@ fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { _ => false, }; - is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) + is_slice || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) } 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 method.ident.name == sym::clone; if args.len() == 1; if let Some(arg) = args.get(0); then { arg } else { expr } @@ -1355,7 +1355,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type); if path.ident.name.as_str() == "push"; then { return Some((self_expr, pushed_item)) @@ -1736,7 +1736,7 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: /// Checks for `for` loops over `Option`s and `Result`s. fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) { + if is_type_diagnostic_item(cx, ty, sym::option_type) { span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -1753,7 +1753,7 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { snippet(cx, arg.span, "_") ), ); - } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, ty, sym::result_type) { span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -2186,8 +2186,8 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind; - if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) + || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); if !self.check(&args[1], &args[0], expr); then { return } } @@ -2333,7 +2333,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { // will allow further borrows afterwards let ty = cx.typeck_results().expr_ty(e); is_iterable_array(ty, cx) || - is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + is_type_diagnostic_item(cx, ty, sym::vec_type) || match_type(cx, ty, &paths::LINKED_LIST) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || is_type_diagnostic_item(cx, ty, sym!(hashset_type)) || @@ -2890,7 +2890,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); then { let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 9c623821fdd..a1450b0d5fe 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -5,7 +5,7 @@ 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; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -83,9 +83,9 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } fn is_doc_hidden(attr: &Attribute) -> bool { - attr.has_name(sym!(doc)) + attr.has_name(sym::doc) && match attr.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym!(hidden)), + Some(l) => attr::list_contains_name(&l, sym::hidden), None => false, } } @@ -102,7 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive))); + if !item.attrs.iter().any(|attr| attr.has_name(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 { @@ -154,7 +154,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive))); + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); let header_span = find_header_span(cx, item, data); if let Some(snippet) = snippet_opt(cx, header_span); then { diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 22aa37e41fe..9e2c6c7f231 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -8,6 +8,7 @@ use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -97,9 +98,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) { + if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) { Some(Case::Option) - } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) { + } else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) { Some(Case::Result) } else { None diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 6d1c2ffbfbd..034cd99a9be 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if args.len() == 2; if method.ident.as_str() == "map"; let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::ITERATOR); + if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index d4c2e66ff4b..6b782385a38 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function. @@ -65,8 +66,8 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a if args.len() == 2 && method.ident.as_str() == "map"; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if match_trait_method(cx, expr, &paths::ITERATOR) - || is_type_diagnostic_item(cx, caller_ty, sym!(result_type)) - || is_type_diagnostic_item(cx, caller_ty, sym!(option_type)); + || is_type_diagnostic_item(cx, caller_ty, sym::result_type) + || is_type_diagnostic_item(cx, caller_ty, sym::option_type); then { Some(args) } else { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 076ef235b8b..e50d11a4d71 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function @@ -206,9 +207,9 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr let var_arg = &map_args[0]; let (map_type, variant, lint) = - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(option_type)) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::option_type) { ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(result_type)) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::result_type) { ("Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 331b6c6c34a..086dae9422f 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`. @@ -90,7 +91,7 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym!(vec_type)) + is_type_diagnostic_item(cx, ty, sym::vec_type) } fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4bdfca1a292..c6dca54e250 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -22,7 +22,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -662,7 +662,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp } } else { // not a block, don't lint - return; + return; }; let ty = cx.typeck_results().expr_ty(ex); @@ -840,7 +840,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); - if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) { + if is_type_diagnostic_item(cx, ex_ty, sym::result_type) { for arm in arms { if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); @@ -1509,6 +1509,7 @@ mod redundant_pattern_match { use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; + use rustc_span::sym; pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { @@ -1552,7 +1553,7 @@ mod redundant_pattern_match { if_chain! { if keyword == "while"; if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; - if method_path.ident.name == sym!(next); + if method_path.ident.name == sym::next; if match_trait_method(cx, op, &paths::ITERATOR); then { return; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 5dae7efad97..c83b6f2c329 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; /// Checks for the `INEFFICIENT_TO_STRING` lint pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { @@ -50,7 +51,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, ty, sym::string_type) { return true; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..167b8cc2422 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1677,7 +1677,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, item.span); - if item.ident.name == sym!(new); + if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); @@ -1767,7 +1767,7 @@ fn lint_or_fun_call<'tcx>( _ => (), } - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { + if is_type_diagnostic_item(cx, ty, sym::vec_type) { return; } } @@ -1864,11 +1864,11 @@ fn lint_expect_fun_call( hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, hir::ExprKind::MethodCall(method_name, _, call_args, _) => { if call_args.len() == 1 - && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref)) + && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) && { let arg_type = cx.typeck_results().expr_ty(&call_args[0]); let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) + *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type) } { &call_args[0] @@ -1886,7 +1886,7 @@ fn lint_expect_fun_call( // converted to string. fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, arg_ty, sym::string_type) { return false; } if let ty::Ref(_, ty, ..) = arg_ty.kind() { @@ -1973,9 +1973,9 @@ fn lint_expect_fun_call( } let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) { + let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) { "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) { "|_|" } else { return; @@ -2162,7 +2162,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { "" - } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { "&" } else { return; @@ -2188,14 +2188,14 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, obj_ty, sym::string_type) { lint_string_extend(cx, expr, args); } } fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym!(vec_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])); if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); @@ -2348,7 +2348,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ ); } } - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type) || matches!( &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), ty::Array(_, _) @@ -2381,7 +2381,7 @@ fn lint_iter_nth<'tcx>( let mut_str = if is_mut { "_mut" } else { "" }; let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vec_type)) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { "Vec" } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { "VecDeque" @@ -2434,7 +2434,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() { needs_ref = get_args_str.parse::().is_ok(); "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) { + } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) { needs_ref = get_args_str.parse::().is_ok(); "Vec" } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) { @@ -2520,7 +2520,7 @@ fn derefs_to_slice<'tcx>( match ty.kind() { ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), + ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), ty::Array(_, size) => size .try_eval_usize(cx.tcx, cx.param_env) .map_or(false, |size| size < 32), @@ -2530,7 +2530,7 @@ fn derefs_to_slice<'tcx>( } if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - if path.ident.name == sym!(iter) && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { + if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { Some(&args[0]) } else { None @@ -2555,9 +2555,9 @@ fn derefs_to_slice<'tcx>( fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((UNWRAP_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { Some((UNWRAP_USED, "a Result", "Err")) } else { None @@ -2607,7 +2607,7 @@ fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::E fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) { if_chain! { // lint if the caller of `ok()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym!(result_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type); let result_type = cx.typeck_results().expr_ty(&ok_args[0]); if let Some(error_type) = get_error_type(cx, result_type); if has_debug_impl(error_type, cx); @@ -2637,7 +2637,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type) }, _ => false, }; @@ -2663,7 +2663,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } // lint if caller of `.map().flatten()` is an Option - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { let func_snippet = snippet(cx, map_args[1].span, ".."); let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( @@ -2687,8 +2687,8 @@ fn lint_map_unwrap_or_else<'tcx>( unwrap_args: &'tcx [hir::Expr<'_>], ) -> bool { // lint if the caller of `map()` is an `Option` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); if is_option || is_result { // Don't make a suggestion that may fail to compile due to mutably borrowing @@ -2741,8 +2741,8 @@ fn lint_map_unwrap_or_else<'tcx>( /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` @@ -3100,7 +3100,7 @@ fn lint_chars_cmp( if arg_char.len() == 1; if let hir::ExprKind::Path(ref qpath) = fun.kind; if let Some(segment) = single_segment_path(qpath); - if segment.ident.name == sym!(Some); + if segment.ident.name == sym::Some; then { let mut applicability = Applicability::MachineApplicable; let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); @@ -3375,7 +3375,7 @@ fn lint_option_as_ref_deref<'tcx>( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); - if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) { + if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { return; } @@ -3482,7 +3482,7 @@ fn lint_map_collect( if match_trait_method(cx, map_expr, &paths::ITERATOR); // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym!(result_type)); + if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); if result_t.is_unit(); @@ -3509,7 +3509,7 @@ fn lint_map_collect( /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1), + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::result_type) => substs.types().nth(1), _ => None, } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index d30b85d6a78..7763fd5f113 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -7,7 +7,7 @@ use rustc_hir::{self, HirId, Path}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{sym, Symbol}; use super::MAP_UNWRAP_OR; @@ -20,7 +20,7 @@ pub(super) fn lint<'tcx>( map_span: Span, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) { // Do not lint if the `map` argument uses identifiers in the `map` // argument that are also used in the `unwrap_or` argument diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 08b3eab9b7c..cde89983a26 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -3,6 +3,7 @@ use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::UNNECESSARY_LAZY_EVALUATIONS; @@ -14,8 +15,8 @@ pub(super) fn lint<'tcx>( args: &'tcx [hir::Expr<'_>], simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type); if is_option || is_result { if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 813f9c43948..009e3d8937e 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -14,6 +14,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Warns if there is missing doc for any documentable item @@ -105,10 +106,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { - attr.has_name(sym!(doc)) + attr.has_name(sym::doc) && match attr.meta_item_list() { None => false, - Some(l) => attr::list_contains_name(&l[..], sym!(hidden)), + Some(l) => attr::list_contains_name(&l[..], sym::hidden), } }); self.doc_hidden_stack.push(doc_hidden); @@ -128,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Enum(..) => "an enum", hir::ItemKind::Fn(..) => { // ignore main() - if it.ident.name == sym!(main) { + if it.ident.name == sym::main { let def_id = it.hir_id.owner; let def_key = cx.tcx.hir().def_key(def_id); if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) { diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 3eae45b2819..53abe6086ea 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_lint::{self, LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** it lints if an exported function, method, trait method with default impl, @@ -57,7 +58,7 @@ declare_clippy_lint! { } fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { - let has_inline = attrs.iter().any(|a| a.has_name(sym!(inline))); + let has_inline = attrs.iter().any(|a| a.has_name(sym::inline)); if !has_inline { span_lint( cx, diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index b71d5496a37..405c21d608d 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -112,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.attrs.iter().any(|a| a.has_name(sym!(automatically_derived))) { + if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.hir_id); } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7e933c674dd..5c92590f41e 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::can_type_implement_copy; @@ -204,12 +204,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let deref_span = spans_need_deref.get(&canonical_id); if_chain! { - if is_type_diagnostic_item(cx, ty, sym!(vec_type)); + if is_type_diagnostic_item(cx, ty, sym::vec_type); if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; if let Some(elem_ty) = path.segments.iter() - .find(|seg| seg.ident.name == sym!(Vec)) + .find(|seg| seg.ident.name == sym::Vec) .and_then(|ps| ps.args.as_ref()) .map(|params| params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), @@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, ty, sym::string_type) { if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { diag.span_suggestion( @@ -302,7 +302,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { /// Functions marked with these attributes must have the exact signature. fn requires_exact_signature(attrs: &[Attribute]) -> bool { attrs.iter().any(|attr| { - [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)] + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] .iter() .any(|&allow| attr.has_name(allow)) }) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 28d1322e946..68fdd0eb269 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for types with a `fn new() -> Self` method and no @@ -91,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // impl of `Default` return; } - if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) { + if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) { let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); let self_ty = cx.tcx.type_of(self_def_id); if_chain! { diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 603440c0f83..485888fa944 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -7,6 +7,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_span::symbol::{Ident, Symbol}; use std::cmp::Ordering; @@ -384,7 +385,7 @@ impl EarlyLintPass for NonExpressiveNames { } fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { - if !attrs.iter().any(|attr| attr.has_name(sym!(test))) { + if !attrs.iter().any(|attr| attr.has_name(sym::test)) { let mut visitor = SimilarNamesLocalVisitor { names: Vec::new(), cx, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index eb7624b25a3..681dbce9769 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -66,7 +67,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" - && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type)) + && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) } else { false } diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 4077aba6ef1..72dfccc1089 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. @@ -40,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { hir_id: hir::HirId, ) { if !matches!(fn_kind, FnKind::Closure(_)) - && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) + && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { lint_impl_body(cx, span, body); } diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 19d355e64ca..ceecc8dbc06 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -3,6 +3,7 @@ use if_chain::if_chain; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for manual re-implementations of `PartialEq::ne`. @@ -39,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { if trait_ref.path.res.def_id() == eq_trait; then { for impl_item in impl_items { - if impl_item.ident.name == sym!(ne) { + if impl_item.ident.name == sym::ne { span_lint_hir( cx, PARTIALEQ_NE_IMPL, diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 28816c3076d..030650c3256 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -10,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutabil use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_target::spec::Target; @@ -230,8 +230,8 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } for a in attrs { if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym!(proc_macro_derive)) - || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + if a.has_name(sym::proc_macro_derive) + || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)) { return; } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 6b1c848a946..dcb643a28ae 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::MultiSpan; +use rustc_span::{sym, MultiSpan}; use std::borrow::Cow; declare_clippy_lint! { @@ -181,7 +181,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { + if is_type_diagnostic_item(cx, ty, sym::vec_type) { let mut ty_snippet = None; if_chain! { if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; @@ -225,7 +225,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } - } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + } else if is_type_diagnostic_item(cx, ty, sym::string_type) { if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) { span_lint_and_then( cx, diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 0a2d1b5fbe6..e0996804a59 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -3,6 +3,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use std::fmt; declare_clippy_lint! { @@ -92,7 +93,7 @@ fn expr_as_ptr_offset_call<'tcx>( ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind { if is_expr_ty_raw_ptr(cx, &args[0]) { - if path_segment.ident.name == sym!(offset) { + if path_segment.ident.name == sym::offset { return Some((&args[0], &args[1], Method::Offset)); } if path_segment.ident.name == sym!(wrapping_offset) { diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index dbc676ae224..d9b280b7a85 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,6 +4,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use crate::utils::sugg::Sugg; use crate::utils::{ @@ -143,7 +144,7 @@ impl QuestionMark { fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expression); - is_type_diagnostic_item(cx, expr_ty, sym!(option_type)) + is_type_diagnostic_item(cx, expr_ty, sym::option_type) } fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index de54711d851..79e9a56af9a 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{Span, Spanned}; +use rustc_span::sym; use rustc_span::symbol::Ident; use std::cmp::Ordering; @@ -304,7 +305,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: if_chain! { // `.iter()` call if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; - if iter_path.ident.name == sym!(iter); + if iter_path.ident.name == sym::iter; // range expression in `.zip()` call: `0..x.len()` if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); if is_integer_const(cx, start, 0); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 344ed02361d..b4a9804fb25 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; +use rustc_span::sym; use std::convert::TryFrom; use std::ops::ControlFlow; @@ -115,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD) || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) - && is_type_diagnostic_item(cx, arg_ty, sym!(string_type))); + && is_type_diagnostic_item(cx, arg_ty, sym::string_type)); let from_deref = !from_borrow && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index ae601353009..d34e744eb94 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. @@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + } else if is_type_diagnostic_item(cx, ty, sym::string_type) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index a6e4252a0c8..7f4913a02cb 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -9,6 +9,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; @@ -141,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { } fn attr_is_cfg(attr: &Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) + attr.meta_item_list().is_some() && attr.has_name(sym::cfg) } fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3783bd78de2..0dd2da949c4 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -4,6 +4,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::sym; use if_chain::if_chain; @@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type)) + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::string_type) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 54b38d9f4ce..386987eb181 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -8,6 +8,7 @@ use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for manual swapping. @@ -197,7 +198,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym!(vec_type)) + || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) { return Slice::Swappable(lhs1, idx1, idx2); diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 3e747ec4ad9..6f6b6999bf0 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usages of `Err(x)?`. @@ -133,7 +134,7 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym!(result_type)); + if is_type_diagnostic_item(cx, ty, sym::result_type); let err_ty = subst.type_at(1); then { Some(err_ty) @@ -151,7 +152,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did); + if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did); let err_ty = ready_subst.type_at(1); then { @@ -170,11 +171,11 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did); + if cx.tcx.is_diagnostic_item(sym::option_type, ready_def.did); let some_ty = ready_subst.type_at(0); if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did); + if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did); let err_ty = some_subst.type_at(1); then { diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 45f3bc3ea85..c7d82da3b8b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -522,7 +522,7 @@ impl Types { ); return; // don't recurse into the type } - } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) { + } else if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) { if_chain! { // Get the _ part of Vec<_> if let Some(ref last) = last_path_segment(qpath).args; @@ -559,7 +559,7 @@ impl Types { return; // don't recurse into the type } } - } else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) { + } else if cx.tcx.is_diagnostic_item(sym::option_type, def_id) { if match_type_parameter(cx, qpath, &paths::OPTION).is_some() { span_lint( cx, @@ -1610,7 +1610,7 @@ fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { if names.is_empty() { return false; } - if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { + if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { return true; } } @@ -2777,7 +2777,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't } if match_path(ty_path, &paths::HASHMAP) { - if method.ident.name == sym!(new) { + if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashMap::default()".to_string()); } else if method.ident.name == sym!(with_capacity) { @@ -2790,7 +2790,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't ); } } else if match_path(ty_path, &paths::HASHSET) { - if method.ident.name == sym!(new) { + if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashSet::default()".to_string()); } else if method.ident.name == sym!(with_capacity) { diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 1307237dbc7..0bccfc15678 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -6,6 +6,7 @@ use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegme use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use rustc_span::symbol::Ident; declare_clippy_lint! { @@ -175,7 +176,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type)); + if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index ea4b8172c9c..f4a77e54dd1 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -11,6 +11,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail. @@ -92,11 +93,11 @@ fn collect_unwrap_info<'tcx>( invert: bool, ) -> Vec> { fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name) + is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name) } fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name) + is_type_diagnostic_item(cx, ty, sym::result_type) && ["is_ok", "is_err"].contains(&method_name) } if let ExprKind::Binary(op, left, right) = &expr.kind { @@ -168,8 +169,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { if_chain! { if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind; if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; - if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name); - let call_to_unwrap = method_name.ident.name == sym!(unwrap); + if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); + 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 diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 0f8797243ec..fde31029330 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` @@ -57,8 +57,8 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { // first check if it's a method or function if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::result_type) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::option_type); then { lint_impl_body(cx, impl_item.span, impl_item); } @@ -82,8 +82,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // check for `expect` if let Some(arglists) = method_chain_args(expr, &["expect"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); - if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) - || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type) { self.result.push(expr.span); } @@ -92,8 +92,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); - if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) - || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type) { self.result.push(expr.span); } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 4e4a206a583..c6194b0c6de 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -9,6 +9,7 @@ use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls @@ -106,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, a, sym!(result_type)); + if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); @@ -136,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { then { if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); - if is_type_diagnostic_item(cx, a, sym!(result_type)); + if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 0a8a4a5f9ae..85e7f055e79 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -52,6 +52,7 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; +use rustc_span::sym as rustc_sym; use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; @@ -974,7 +975,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived))) + attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived)) } /// Remove blocks around an expression. diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index d9d60fffcd7..ff414f748ef 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -10,8 +10,7 @@ use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span, Symbol}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -224,7 +223,7 @@ impl EarlyLintPass for Write { .expect("path has at least one segment") .ident .name; - if trait_name == sym!(Debug) { + if trait_name == sym::Debug { self.in_debug_impl = true; } } From b2332a7357b5da7220592aea4c711609eed54ef7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 11:47:17 -0600 Subject: [PATCH 0883/1110] Change lint to use const sym --- clippy_lints/src/utils/internal_lints.rs | 4 ++-- tests/ui/match_type_on_diag_item.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 6ca72d895c8..8b59a9541a7 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -225,7 +225,7 @@ declare_clippy_lint! { /// /// Good: /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type)) + /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type) /// ``` pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, internal, @@ -724,7 +724,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { expr.span, "usage of `utils::match_type()` on a type diagnostic item", "try", - format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), + format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr index 5e5fe9e3a3e..82465dbaf6e 100644 --- a/tests/ui/match_type_on_diag_item.stderr +++ b/tests/ui/match_type_on_diag_item.stderr @@ -2,7 +2,7 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` | note: the lint level is defined here --> $DIR/match_type_on_diag_item.rs:1:9 @@ -15,19 +15,19 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:43:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:46:17 | LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` error: aborting due to 4 previous errors From a6611de75a9c6cf8b0ccfa371491a646a743667f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 12:55:05 -0600 Subject: [PATCH 0884/1110] Include bindings as machine applicable --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index aec1b7e2e46..fe54a238aa4 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -35,7 +35,8 @@ pub(super) fn lint<'tcx>( let applicability = if body .params .iter() - .all(|param| matches!(param.pat.kind, hir::PatKind::Wild)) + // bindings are checked to be unused above + .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) { Applicability::MachineApplicable } else { From 315bab0ea186d5324fba28ccd5db82ecc7996cc4 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 10 Sep 2020 20:14:23 +0200 Subject: [PATCH 0885/1110] Add `from_iter_instead_of_collect` lint implementation --- clippy_lints/src/methods/mod.rs | 59 +++++++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 1 + 2 files changed, 60 insertions(+) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..93b5d4e7efc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1369,6 +1369,38 @@ declare_clippy_lint! { "using `.map(_).collect::()`, which can be replaced with `try_for_each`" } +declare_clippy_lint! { + /// **What it does:** Checks for `from_iter()` function calls that implements `FromIterator` + /// trait. + /// + /// **Why is this bad?** Makes code less readable especially in method chaining. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::iter::FromIterator; + /// + /// let five_fives = std::iter::repeat(5).take(5); + /// + /// let v = Vec::from_iter(five_fives); + /// + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// ``` + /// Use instead: + /// ```rust + /// let five_fives = std::iter::repeat(5).take(5); + /// + /// let v: Vec = five_fives.collect(); + /// + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// ``` + pub FROM_ITER_INSTEAD_OF_COLLECT, + style, + "use `.collect()` instead of `::from_iter()`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1419,6 +1451,7 @@ declare_lint_pass!(Methods => [ OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, MAP_COLLECT_RESULT_UNIT, + FROM_ITER_INSTEAD_OF_COLLECT, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1505,6 +1538,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } match expr.kind { + hir::ExprKind::Call(ref func, ref args) => { + if let hir::ExprKind::Path(path) = &func.kind { + let path_segment = last_path_segment(path); + if path_segment.ident.name.as_str() == "from_iter" { + lint_from_iter(cx, expr, args); + } + } + }, hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); @@ -3831,6 +3872,24 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); } +fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let ty = cx.typeck_results().expr_ty(expr); + let id = get_trait_def_id(cx, &paths::FROM_ITERATOR_TRAIT).unwrap(); + + if implements_trait(cx, ty, id, &[]) { + // `expr` implements `FromIterator` trait + let iter_expr = snippet(cx, args[0].span, ".."); + span_lint_and_help( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "use `.collect()` instead of `::from_iter()`", + None, + &format!("consider using `{}.collect()`", iter_expr), + ); + } +} + fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 736a531eda6..3aade8ca8a2 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,6 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; +pub const FROM_ITERATOR_TRAIT: [&str; 3] = ["std", "iter", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; From 9d6eedf5f2fccfc94f36473012794b2c679d3ad3 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 10 Sep 2020 20:16:28 +0200 Subject: [PATCH 0886/1110] Run `cargo dev update_lints` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 +++ src/lintlist/mod.rs | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..50f03126c7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1732,6 +1732,7 @@ Released 2018-09-13 [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..fa4b6d11623 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -693,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::FILTER_NEXT, &methods::FIND_MAP, &methods::FLAT_MAP_IDENTITY, + &methods::FROM_ITER_INSTEAD_OF_COLLECT, &methods::GET_UNWRAP, &methods::INEFFICIENT_TO_STRING, &methods::INTO_ITER_ON_REF, @@ -1422,6 +1423,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::EXPECT_FUN_CALL), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), + LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITER_CLONED_COLLECT), @@ -1620,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), + LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), LintId::of(&methods::ITER_NEXT_SLICE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..7a1ebc3d2dd 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -753,6 +753,13 @@ vec![ deprecation: None, module: "drop_forget_ref", }, + Lint { + name: "from_iter_instead_of_collect", + group: "style", + desc: "use `.collect()` instead of `::from_iter()`", + deprecation: None, + module: "methods", + }, Lint { name: "future_not_send", group: "nursery", From 1b117f46296b5c15848d02a4433912a604cc9873 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 10 Sep 2020 20:18:23 +0200 Subject: [PATCH 0887/1110] Add tests for `from_iter_instead_of_collect` --- tests/ui/from_iter_instead_of_collect.rs | 15 +++++++++++++++ tests/ui/from_iter_instead_of_collect.stderr | 19 +++++++++++++++++++ tests/ui/from_iter_instead_of_collect.stdout | 0 3 files changed, 34 insertions(+) create mode 100644 tests/ui/from_iter_instead_of_collect.rs create mode 100644 tests/ui/from_iter_instead_of_collect.stderr create mode 100644 tests/ui/from_iter_instead_of_collect.stdout diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs new file mode 100644 index 00000000000..7a1bc64f4bd --- /dev/null +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -0,0 +1,15 @@ +#![warn(clippy::from_iter_instead_of_collect)] + +use std::collections::HashMap; +use std::iter::FromIterator; + +fn main() { + { + let iter_expr = std::iter::repeat(5).take(5); + + Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + //let v: Vec = iter_expr.collect(); + let a: Vec = Vec::new(); + } +} diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr new file mode 100644 index 00000000000..4fadfb658fe --- /dev/null +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -0,0 +1,19 @@ +error: use `.collect()` instead of `::from_iter()` + --> $DIR/from_iter_instead_of_collect.rs:10:5 + | +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` + = help: consider using `iter_expr.collect()` + +error: use `.collect()` instead of `::from_iter()` + --> $DIR/from_iter_instead_of_collect.rs:11:5 + | +LL | HashMap::::from_iter(vec![5,5,5,5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `vec![5,5,5,5].iter().enumerate().collect()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/from_iter_instead_of_collect.stdout b/tests/ui/from_iter_instead_of_collect.stdout new file mode 100644 index 00000000000..e69de29bb2d From 8a5d78b71a71317314d7dccef7c73b523f0a44c2 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Fri, 11 Sep 2020 13:29:52 +0200 Subject: [PATCH 0888/1110] Fix `from_iter_instead_of_collect` lint crashing on exprs without path segment --- clippy_lints/src/methods/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 93b5d4e7efc..c0c39dae865 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1540,8 +1540,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { match expr.kind { hir::ExprKind::Call(ref func, ref args) => { if let hir::ExprKind::Path(path) = &func.kind { - let path_segment = last_path_segment(path); - if path_segment.ident.name.as_str() == "from_iter" { + if match_qpath(path, &["from_iter"]) { lint_from_iter(cx, expr, args); } } From a85670652a37238db7328b5eaab7cb7794fa40c8 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 12:36:17 +0200 Subject: [PATCH 0889/1110] Update: stderr message format --- tests/ui/from_iter_instead_of_collect.stderr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 4fadfb658fe..cfb92dfbbfd 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,19 +1,19 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:10:5 + --> $DIR/from_iter_instead_of_collect.rs:10:9 | -LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` = help: consider using `iter_expr.collect()` error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:11:5 + --> $DIR/from_iter_instead_of_collect.rs:11:9 | -LL | HashMap::::from_iter(vec![5,5,5,5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider using `vec![5,5,5,5].iter().enumerate().collect()` + = help: consider using `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors From 8906040445a6700be2aaf40ec09937b84dea5d6a Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:04:05 +0200 Subject: [PATCH 0890/1110] Improvements from PR feedback --- clippy_lints/src/methods/mod.rs | 14 ++++++++------ clippy_lints/src/utils/paths.rs | 2 +- tests/ui/from_iter_instead_of_collect.rs | 10 +++------- tests/ui/from_iter_instead_of_collect.stderr | 15 ++++++--------- tests/ui/from_iter_instead_of_collect.stdout | 0 5 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 tests/ui/from_iter_instead_of_collect.stdout diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0c39dae865..70be8909c43 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1370,10 +1370,11 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `from_iter()` function calls that implements `FromIterator` + /// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` /// trait. /// - /// **Why is this bad?** Makes code less readable especially in method chaining. + /// **Why is this bad?** It is recommended style to use collect. See + /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) /// /// **Known problems:** None. /// @@ -3873,18 +3874,19 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); - let id = get_trait_def_id(cx, &paths::FROM_ITERATOR_TRAIT).unwrap(); + let id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); if implements_trait(cx, ty, id, &[]) { // `expr` implements `FromIterator` trait let iter_expr = snippet(cx, args[0].span, ".."); - span_lint_and_help( + span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, "use `.collect()` instead of `::from_iter()`", - None, - &format!("consider using `{}.collect()`", iter_expr), + "consider using", + format!("`{}.collect()`", iter_expr), + Applicability::MaybeIncorrect ); } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 3aade8ca8a2..8afbd8930b6 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,7 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; -pub const FROM_ITERATOR_TRAIT: [&str; 3] = ["std", "iter", "FromIterator"]; +pub const FROM_ITERATOR: [&str; 3] = ["std", "iter", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 7a1bc64f4bd..9071be33c64 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -4,12 +4,8 @@ use std::collections::HashMap; use std::iter::FromIterator; fn main() { - { - let iter_expr = std::iter::repeat(5).take(5); + let iter_expr = std::iter::repeat(5).take(5); - Vec::from_iter(iter_expr); - HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - //let v: Vec = iter_expr.collect(); - let a: Vec = Vec::new(); - } + Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index cfb92dfbbfd..1bc787aa795 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,19 +1,16 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:10:9 + --> $DIR/from_iter_instead_of_collect.rs:9:5 | -LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` - = help: consider using `iter_expr.collect()` error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:11:9 + --> $DIR/from_iter_instead_of_collect.rs:10:5 | -LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `vec![5, 5, 5, 5].iter().enumerate().collect()` +LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``vec![5, 5, 5, 5].iter().enumerate().collect()`` error: aborting due to 2 previous errors diff --git a/tests/ui/from_iter_instead_of_collect.stdout b/tests/ui/from_iter_instead_of_collect.stdout deleted file mode 100644 index e69de29bb2d..00000000000 From 0ab96ba2c0196558dc49624f59e6fd9c717a02f6 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:17:50 +0200 Subject: [PATCH 0891/1110] Allow lint --- tests/ui/get_unwrap.fixed | 2 +- tests/ui/get_unwrap.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index 97e6b20f471..924c02a4054 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused_mut)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] #![deny(clippy::get_unwrap)] use std::collections::BTreeMap; diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index 1c9a71c0969..c0c37bb7206 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused_mut)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] #![deny(clippy::get_unwrap)] use std::collections::BTreeMap; From e320dd30428aeb85577b36afa594429a41bc07f6 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:34:36 +0200 Subject: [PATCH 0892/1110] Improve: error message --- clippy_lints/src/utils/paths.rs | 2 +- tests/ui/from_iter_instead_of_collect.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 8afbd8930b6..95fe8733421 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,7 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; -pub const FROM_ITERATOR: [&str; 3] = ["std", "iter", "FromIterator"]; +pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 9071be33c64..25b87a0a903 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -5,7 +5,7 @@ use std::iter::FromIterator; fn main() { let iter_expr = std::iter::repeat(5).take(5); - Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 1bc787aa795..3263005d7ec 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,5 +1,5 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:9:5 + --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` From 854f2cef06f60b37a3176b17b27c1104692f6255 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:36:49 +0200 Subject: [PATCH 0893/1110] Run `cargo dev fmt` --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70be8909c43..013ed67b8e5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3886,7 +3886,7 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< "use `.collect()` instead of `::from_iter()`", "consider using", format!("`{}.collect()`", iter_expr), - Applicability::MaybeIncorrect + Applicability::MaybeIncorrect, ); } } From abdb7aeb55461f0355829be49a09ed86af066ae8 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:44:12 +0200 Subject: [PATCH 0894/1110] Remove backticks --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 013ed67b8e5..2119cb28fc3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3885,7 +3885,7 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< expr.span, "use `.collect()` instead of `::from_iter()`", "consider using", - format!("`{}.collect()`", iter_expr), + format!("{}.collect()", iter_expr), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 3263005d7ec..7f248beadbe 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -2,7 +2,7 @@ error: use `.collect()` instead of `::from_iter()` --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `iter_expr.collect()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` @@ -10,7 +10,7 @@ error: use `.collect()` instead of `::from_iter()` --> $DIR/from_iter_instead_of_collect.rs:10:5 | LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``vec![5, 5, 5, 5].iter().enumerate().collect()`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors From f359fb872b405fca196f40eadd341d1d06f1fb8b Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 19:04:19 +0200 Subject: [PATCH 0895/1110] Improve error message --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2119cb28fc3..fde43f0055d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3883,8 +3883,8 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, + "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", - "consider using", format!("{}.collect()", iter_expr), Applicability::MaybeIncorrect, ); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 7f248beadbe..46bdc2f4e19 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,16 +1,16 @@ -error: use `.collect()` instead of `::from_iter()` +error: usage of `FromIterator::from_iter` --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `iter_expr.collect()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` -error: use `.collect()` instead of `::from_iter()` +error: usage of `FromIterator::from_iter` --> $DIR/from_iter_instead_of_collect.rs:10:5 | LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `vec![5, 5, 5, 5].iter().enumerate().collect()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors From 52d1ea3c9ad8ea97350ba7a0ca0a8e172cfcae78 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Mon, 12 Oct 2020 17:27:50 +0200 Subject: [PATCH 0896/1110] Fix: Don't show lint for types that doesn't implement Iterator --- clippy_lints/src/methods/mod.rs | 7 +++++-- tests/ui/from_iter_instead_of_collect.rs | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fde43f0055d..521b151f5e1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3874,9 +3874,12 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); - let id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); + let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if implements_trait(cx, ty, id, &[]) { + let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); + let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap(); + + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) { // `expr` implements `FromIterator` trait let iter_expr = snippet(cx, args[0].span, ".."); span_lint_and_sugg( diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 25b87a0a903..045eb3133d3 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -8,4 +8,6 @@ fn main() { Vec::from_iter(iter_expr); HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + + Vec::from_iter(vec![42u32]); } From ddf23d649ae02dde7aed22ec6699a736492b7184 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Sat, 24 Oct 2020 13:53:09 +0200 Subject: [PATCH 0897/1110] Fix: Use `.collect()` instead of `::fromIterator()` --- clippy_lints/src/lifetimes.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index c8a5a9c9431..4d737b3f49b 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -16,7 +16,6 @@ use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; -use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -214,14 +213,15 @@ fn could_use_elision<'tcx>( } if allowed_lts - .intersection(&FxHashSet::from_iter( - input_visitor + .intersection( + &input_visitor .nested_elision_site_lts .iter() .chain(output_visitor.nested_elision_site_lts.iter()) .cloned() - .filter(|v| matches!(v, RefLt::Named(_))), - )) + .filter(|v| matches!(v, RefLt::Named(_))) + .collect(), + ) .next() .is_some() { From cf2043d4a255b589f329e0727ef2e3a10e1a401d Mon Sep 17 00:00:00 2001 From: Randall Mason Date: Tue, 3 Nov 2020 15:59:24 -0600 Subject: [PATCH 0898/1110] Update wording to avoid code having "lint" metaphor --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 36a816b9ee6..8a5975e1f97 100644 --- a/README.md +++ b/README.md @@ -167,11 +167,10 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: * `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. -Note: `allow` in this case means to "allow your code to have the lint without -warning". `deny` means "produce an error if your code has the lint". `warn` -means "produce a warning, but don't produce an error due to this lint". An -error causes clippy to exit with an error code, so is useful in scripts like -CI/CD. +Note: `allow` means to suppress the lint for your code. With `warn` the lint +will only emit a warning, while with `deny` the lint will emit an error, when +triggering for your code. An error causes clippy to exit with an error code, so +is useful in scripts like CI/CD. If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra flags to Clippy during the run: From f5166e81b1065983de15f883ae04a81d30f75edf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 4 Nov 2020 22:41:15 +0100 Subject: [PATCH 0899/1110] Run cargo dev fmt --- clippy_lints/src/consts.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index c8bbc9ce2b0..0035ded9356 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -7,10 +7,10 @@ use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, ScalarInt}; -use rustc_middle::{bug, span_bug}; use rustc_middle::mir::interpret::Scalar; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; use std::convert::TryInto; @@ -501,7 +501,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn miri_to_const(result: &ty::Const<'_>) -> Option { - use rustc_middle::mir::interpret::{ConstValue}; + use rustc_middle::mir::interpret::ConstValue; match result.val { ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { match result.ty.kind() { From a4acb3164ad93d8fe444783ad588a8b5d71dacd9 Mon Sep 17 00:00:00 2001 From: Urcra Date: Wed, 4 Nov 2020 23:39:52 +0100 Subject: [PATCH 0900/1110] Fix example for cargo common data --- clippy_lints/src/cargo_common_metadata.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 76a000157df..0d294761af5 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -23,6 +23,21 @@ declare_clippy_lint! { /// [package] /// name = "clippy" /// version = "0.0.212" + /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" + /// repository = "https://github.com/rust-lang/rust-clippy" + /// readme = "README.md" + /// license = "MIT OR Apache-2.0" + /// keywords = ["clippy", "lint", "plugin"] + /// categories = ["development-tools", "development-tools::cargo-plugins"] + /// ``` + /// + /// Should include an authors field like: + /// + /// ```toml + /// # This `Cargo.toml` includes all common metadata + /// [package] + /// name = "clippy" + /// version = "0.0.212" /// authors = ["Someone "] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" From fd8decee6ac64caf05e35974e652a625d6105eed Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 3 Nov 2020 11:44:26 -0600 Subject: [PATCH 0901/1110] Readme improvements Format lint categories as a table with the default lint level. --- README.md | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8a5975e1f97..1da626b505d 100644 --- a/README.md +++ b/README.md @@ -7,28 +7,22 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g [There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) -We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: +Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). +You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. -* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`) -* `clippy::correctness` (code that is just **outright wrong** or **very very useless**, causes hard errors by default) -* `clippy::style` (code that should be written in a more idiomatic way) -* `clippy::complexity` (code that does something simple but in a complex way) -* `clippy::perf` (code that can be written in a faster way) -* `clippy::pedantic` (lints which are rather strict, off by default) -* `clippy::nursery` (new lints that aren't quite ready yet, off by default) -* `clippy::cargo` (checks against the cargo manifest, off by default) +Category | Description | Default level +-- | -- | -- +`clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** +`clippy::correctness` | code that is outright wrong or very useless | **deny** +`clippy::style` | code that should be written in a more idiomatic way | **warn** +`clippy::complexity` | code that does something simple but in a complex way | **warn** +`clippy::perf` | code that can be written to run faster | **warn** +`clippy::pedantic` | lints which are rather strict or might have false positives | allow +`clippy::nursery` | new lints that are still under development | allow +`clippy::cargo` | lints for the cargo manifest | allow More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! -Only the following of those categories are enabled by default: - -* `clippy::style` -* `clippy::correctness` -* `clippy::complexity` -* `clippy::perf` - -Other categories need to be enabled in order for their lints to be executed. - The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used very selectively, if at all. From bc27d1492d60ecfb0673629e28bc4bbe0b7fd886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Jos=C3=A9=20Pereira?= Date: Thu, 8 Oct 2020 00:19:56 -0300 Subject: [PATCH 0902/1110] Add string_from_utf8_as_bytes linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/strings.rs | 68 ++++++++++++++++++++++- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 +++ tests/ui/string_from_utf8_as_bytes.fixed | 6 ++ tests/ui/string_from_utf8_as_bytes.rs | 6 ++ tests/ui/string_from_utf8_as_bytes.stderr | 10 ++++ 8 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/ui/string_from_utf8_as_bytes.fixed create mode 100644 tests/ui/string_from_utf8_as_bytes.rs create mode 100644 tests/ui/string_from_utf8_as_bytes.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..aeccf6cf83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1952,6 +1952,7 @@ Released 2018-09-13 [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign [`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..d29ba33064c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -826,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, + &strings::STRING_FROM_UTF8_AS_BYTES, &strings::STRING_LIT_AS_BYTES, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, @@ -1515,6 +1516,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), + LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1738,6 +1740,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), + LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3783bd78de2..a55df1e5502 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,5 +1,5 @@ use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -8,7 +8,10 @@ use rustc_span::source_map::Spanned; use if_chain::if_chain; use crate::utils::SpanlessEq; -use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg}; +use crate::utils::{ + get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, + span_lint_and_sugg, +}; declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without @@ -173,16 +176,75 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { } } +declare_clippy_lint! { + /// **What it does:** Check if the string is transformed to byte array and casted back to string. + /// + /// **Why is this bad?** It's unnecessary, the string can be used directly. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); + /// ``` + /// could be written as + /// ```rust + /// let _ = &"Hello World!"[6..11]; + /// ``` + pub STRING_FROM_UTF8_AS_BYTES, + complexity, + "casting string slices to byte slices and back" +} + // Max length a b"foo" string can take const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; -declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]); +declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use crate::utils::{snippet, snippet_with_applicability}; use rustc_ast::LitKind; + if_chain! { + // Find std::str::converts::from_utf8 + if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); + + // Find string::as_bytes + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind; + if let ExprKind::Index(ref left, ref right) = args.kind; + let (method_names, expressions, _) = method_calls(left, 1); + if method_names.len() == 1; + if expressions.len() == 1; + if expressions[0].len() == 1; + if method_names[0] == sym!(as_bytes); + + // Check for slicer + if let ExprKind::Struct(ref path, _, _) = right.kind; + if let QPath::LangItem(LangItem::Range, _) = path; + + then { + let mut applicability = Applicability::MachineApplicable; + let string_expression = &expressions[0][0]; + + let snippet_app = snippet_with_applicability( + cx, + string_expression.span, "..", + &mut applicability, + ); + + span_lint_and_sugg( + cx, + STRING_FROM_UTF8_AS_BYTES, + e.span, + "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", + "try", + format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")), + applicability + ) + } + } + if_chain! { if let ExprKind::MethodCall(path, _, args, _) = &e.kind; if path.ident.name == sym!(as_bytes); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd72fdd61fd..0d0ecf9ae9f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -120,6 +120,7 @@ pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..4d824cf94be 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2230,6 +2230,13 @@ vec![ deprecation: None, module: "methods", }, + Lint { + name: "string_from_utf8_as_bytes", + group: "complexity", + desc: "casting string slices to byte slices and back", + deprecation: None, + module: "strings", + }, Lint { name: "string_lit_as_bytes", group: "nursery", diff --git a/tests/ui/string_from_utf8_as_bytes.fixed b/tests/ui/string_from_utf8_as_bytes.fixed new file mode 100644 index 00000000000..6e665cdd563 --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.fixed @@ -0,0 +1,6 @@ +// run-rustfix +#![warn(clippy::string_from_utf8_as_bytes)] + +fn main() { + let _ = Some(&"Hello World!"[6..11]); +} diff --git a/tests/ui/string_from_utf8_as_bytes.rs b/tests/ui/string_from_utf8_as_bytes.rs new file mode 100644 index 00000000000..670d206d367 --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.rs @@ -0,0 +1,6 @@ +// run-rustfix +#![warn(clippy::string_from_utf8_as_bytes)] + +fn main() { + let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); +} diff --git a/tests/ui/string_from_utf8_as_bytes.stderr b/tests/ui/string_from_utf8_as_bytes.stderr new file mode 100644 index 00000000000..bf5e5d33e8f --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.stderr @@ -0,0 +1,10 @@ +error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary + --> $DIR/string_from_utf8_as_bytes.rs:5:13 + | +LL | let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&"Hello World!"[6..11])` + | + = note: `-D clippy::string-from-utf8-as-bytes` implied by `-D warnings` + +error: aborting due to previous error + From f83762b79cf23bfa91d77d6f04ec2b87b2159b07 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 18:03:16 +0100 Subject: [PATCH 0903/1110] Skip rustfmt as it is wanted for this test --- clippy_lints/src/reference.rs | 23 ++++++++++------------- clippy_lints/src/try_err.rs | 6 ------ tests/ui/deref_addrof.fixed | 1 + tests/ui/deref_addrof.rs | 1 + tests/ui/deref_addrof.stderr | 4 ++-- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 8646d616735..35a1310d68b 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,11 +1,10 @@ use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; +use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::BytePos; -// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -53,31 +52,29 @@ impl EarlyLintPass for DerefAddrOf { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` let trim_leading_whitespaces = |span| { - if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snippet_opt(cx, span).and_then(|snip| { + #[allow(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()).map(|pos| { span.lo() + BytePos(pos as u32) }) - }) { - e.span.with_lo(start_no_whitespace) - } else { - span - } + }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) }; let rpos = if *mutability == Mutability::Mut { macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() } else { - macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + macro_source.rfind('&').expect("already checked this is a reference") + "&".len() }; + #[allow(clippy::cast_possible_truncation)] let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + snippet_with_applicability(cx, span, "_", &mut applicability) } else { - snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + snippet_with_applicability(cx, e.span, "_", &mut applicability) } } else { - snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() - }; + snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability) + }.to_string(); span_lint_and_sugg( cx, DEREF_ADDROF, diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e6d8e7521a8..9c1185d30f2 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -92,13 +92,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - // println!("\n\n{:?}", in_macro(expr.span)); - // println!("{:#?}", snippet(cx, err_arg.span, "_")); let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { - // println!("from expansion"); snippet_with_macro_callsite(cx, err_arg.span, "_") } else { - // println!("just a snippet"); snippet(cx, err_arg.span, "_") }; let suggestion = if err_ty == expr_err_ty { @@ -106,8 +102,6 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; - // println!("origin_snippet: {:#?}", origin_snippet); - // println!("suggestion: {:#?}", suggestion); span_lint_and_sugg( cx, diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 689e9d55223..0795900558b 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -38,6 +38,7 @@ fn main() { let b = *aref; } +#[rustfmt::skip] macro_rules! m { ($visitor: expr) => { $visitor diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 57effce5d12..60c4318601b 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -38,6 +38,7 @@ fn main() { let b = **&aref; } +#[rustfmt::skip] macro_rules! m { ($visitor: expr) => { *& $visitor diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 52c1f7d1da8..e85b30fa56e 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -49,7 +49,7 @@ LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:43:9 + --> $DIR/deref_addrof.rs:44:9 | LL | *& $visitor | ^^^^^^^^^^^ help: try this: `$visitor` @@ -60,7 +60,7 @@ LL | m!(self) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:50:9 + --> $DIR/deref_addrof.rs:51:9 | LL | *& mut $visitor | ^^^^^^^^^^^^^^^ help: try this: `$visitor` From 83e75f92079909aa07306633f26f42eccfb608e1 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 5 Nov 2020 18:00:34 +0100 Subject: [PATCH 0904/1110] Fix incorrect suggestion for `try_err` lint when `Err` arg is itself a macro --- clippy_lints/src/try_err.rs | 9 ++++++--- tests/ui/try_err.fixed | 18 ++++++++++++++++++ tests/ui/try_err.rs | 18 ++++++++++++++++++ tests/ui/try_err.stderr | 21 ++++++++++++++++----- 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 9c1185d30f2..e1dec63a98a 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,6 +1,6 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, - span_lint_and_sugg, + differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -91,8 +91,11 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { }; let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let differing_contexts = differing_macro_contexts(expr.span, err_arg.span); - let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { + let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts { + snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_") + } else if err_arg.span.from_expansion() && !in_macro(expr.span) { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 053dd45f23e..aa43e69f79e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -88,8 +88,26 @@ macro_rules! try_validation { }}; } +macro_rules! ret_one { + () => { + 1 + }; +} + +macro_rules! try_validation_in_macro { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => return Err(ret_one!()), + } + }}; +} + fn calling_macro() -> Result { + // macro try_validation!(Ok::<_, i32>(5)); + // `Err` arg is another macro + try_validation_in_macro!(Ok::<_, i32>(5)); Ok(5) } diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 215ca6a07e6..df3a9dc5367 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -88,8 +88,26 @@ macro_rules! try_validation { }}; } +macro_rules! ret_one { + () => { + 1 + }; +} + +macro_rules! try_validation_in_macro { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => Err(ret_one!())?, + } + }}; +} + fn calling_macro() -> Result { + // macro try_validation!(Ok::<_, i32>(5)); + // `Err` arg is another macro + try_validation_in_macro!(Ok::<_, i32>(5)); Ok(5) } diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 443c8d08472..3905ed2476b 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -40,28 +40,39 @@ LL | try_validation!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:122:9 + --> $DIR/try_err.rs:101:23 + | +LL | Err(_) => Err(ret_one!())?, + | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` +... +LL | try_validation_in_macro!(Ok::<_, i32>(5)); + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:140:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:129:9 + --> $DIR/try_err.rs:147:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:131:9 + --> $DIR/try_err.rs:149:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:139:9 + --> $DIR/try_err.rs:157:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors From 1e4ce0fcaa2bd99ce51e1857767788cc498e62c0 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 6 Nov 2020 04:02:41 +0900 Subject: [PATCH 0905/1110] Fix `await_holding_refcell_ref` examples for clarify --- clippy_lints/src/await_holding_invalid.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index fcebb54c6c2..ca819663fde 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -65,8 +65,8 @@ declare_clippy_lint! { /// use std::cell::RefCell; /// /// async fn foo(x: &RefCell) { - /// let b = x.borrow_mut()(); - /// *ref += 1; + /// let mut y = x.borrow_mut(); + /// *y += 1; /// bar.await; /// } /// ``` @@ -77,8 +77,8 @@ declare_clippy_lint! { /// /// async fn foo(x: &RefCell) { /// { - /// let b = x.borrow_mut(); - /// *ref += 1; + /// let mut y = x.borrow_mut(); + /// *y += 1; /// } /// bar.await; /// } From 1624b00bde42a674c50a03e63868e8b4d08b6b49 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 5 Nov 2020 13:56:57 +0900 Subject: [PATCH 0906/1110] Fix suggestion to add unneeded space in `manual_async` --- clippy_lints/src/manual_async_fn.rs | 17 ++++++- tests/ui/manual_async_fn.fixed | 14 +++++- tests/ui/manual_async_fn.rs | 20 ++++++++ tests/ui/manual_async_fn.stderr | 74 ++++++++++++++++++++++++++--- 4 files changed, 115 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 864d1ea87f5..e9d65abb443 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -69,7 +69,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |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_pos) = header_snip.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec = header_snip.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); @@ -194,7 +207,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, }, _ => { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip))) + snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip))) }, } } diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 4f551690c43..5184f6fdb88 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -7,7 +7,19 @@ use std::future::Future; async fn fut() -> i32 { 42 } -async fn empty_fut() {} +#[rustfmt::skip] +async fn fut2() -> i32 { 42 } + +#[rustfmt::skip] +async fn fut3() -> i32 { 42 } + +async fn empty_fut() {} + +#[rustfmt::skip] +async fn empty_fut2() {} + +#[rustfmt::skip] +async fn empty_fut3() {} async fn core_fut() -> i32 { 42 } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index 6ed60309947..68c0e591f0b 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -9,10 +9,30 @@ fn fut() -> impl Future { async { 42 } } +#[rustfmt::skip] +fn fut2() ->impl Future { + async { 42 } +} + +#[rustfmt::skip] +fn fut3()-> impl Future { + async { 42 } +} + fn empty_fut() -> impl Future { async {} } +#[rustfmt::skip] +fn empty_fut2() ->impl Future { + async {} +} + +#[rustfmt::skip] +fn empty_fut3()-> impl Future { + async {} +} + fn core_fut() -> impl core::future::Future { async move { 42 } } diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index ccd82867427..fdd43db3255 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -15,14 +15,44 @@ LL | fn fut() -> impl Future { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:12:1 + --> $DIR/manual_async_fn.rs:13:1 + | +LL | fn fut2() ->impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn fut2() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut2() ->impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:18:1 + | +LL | fn fut3()-> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn fut3() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut3()-> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:22:1 | LL | fn empty_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and remove the return type | -LL | async fn empty_fut() { +LL | async fn empty_fut() { | ^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | @@ -30,7 +60,37 @@ LL | fn empty_fut() -> impl Future {} | ^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:16:1 + --> $DIR/manual_async_fn.rs:27:1 + | +LL | fn empty_fut2() ->impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut2() { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut2() ->impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:32:1 + | +LL | fn empty_fut3()-> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut3() { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut3()-> impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:36:1 | LL | fn core_fut() -> impl core::future::Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +105,7 @@ LL | fn core_fut() -> impl core::future::Future { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:38:5 + --> $DIR/manual_async_fn.rs:58:5 | LL | fn inh_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +125,7 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:73:1 + --> $DIR/manual_async_fn.rs:93:1 | LL | fn elided(_: &i32) -> impl Future + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +140,7 @@ LL | fn elided(_: &i32) -> impl Future + '_ { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:82:1 + --> $DIR/manual_async_fn.rs:102:1 | LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,5 +154,5 @@ help: move the body of the async block to the enclosing function LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } | ^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 10 previous errors From 5f57608604bd35bd11c1f33cbd7202250e072b54 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Fri, 6 Nov 2020 14:38:46 +0300 Subject: [PATCH 0907/1110] do not trigger map_clone in the case of &mut --- clippy_lints/src/map_clone.rs | 8 +++++--- tests/ui/map_clone.fixed | 8 ++++++++ tests/ui/map_clone.rs | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 034cd99a9be..9a00608ce39 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -80,9 +80,11 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { let obj_ty = cx.typeck_results().expr_ty(&obj[0]); - if let ty::Ref(_, ty, _) = obj_ty.kind() { - let copy = is_copy(cx, ty); - lint(cx, e.span, args[0].span, copy); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, ty); + lint(cx, e.span, args[0].span, copy); + } } else { lint_needless_cloning(cx, e.span, args[0].span); } diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 81c7f659efb..6e3a8e67e81 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -44,4 +44,12 @@ fn main() { let v = vec![&mut d]; let _: Vec = v.into_iter().map(|&mut x| x).collect(); } + + // Issue #6299 + { + let mut aa = 5; + let mut bb = 3; + let items = vec![&mut aa, &mut bb]; + let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 8ed164f0ed5..6fd395710d4 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -44,4 +44,12 @@ fn main() { let v = vec![&mut d]; let _: Vec = v.into_iter().map(|&mut x| x).collect(); } + + // Issue #6299 + { + let mut aa = 5; + let mut bb = 3; + let items = vec![&mut aa, &mut bb]; + let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + } } From b7892c6a26adf6173758641d3f7deb0ae972959d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 6 Nov 2020 22:54:38 +0900 Subject: [PATCH 0908/1110] Refactor to make getting position just before RArrow a common function --- clippy_lints/src/manual_async_fn.rs | 17 ++--------------- clippy_lints/src/unused_unit.rs | 29 ++++++++--------------------- clippy_lints/src/utils/mod.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index e9d65abb443..7b3b450ef93 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use crate::utils::paths::FUTURE_FROM_GENERATOR; -use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then}; +use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -69,20 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = header_snip.rfind("->").map(|rpos| { - let mut rpos = rpos; - let chars: Vec = header_snip.chars().collect(); - while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } - } - break; - } - rpos - }); + if let Some(ret_pos) = position_before_rarrow(header_snip.clone()); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index b1339c3d639..f61fd2ecd73 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::span_lint_and_sugg; +use crate::utils::{position_before_rarrow, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -120,26 +120,13 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source - .rfind("->") - .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - let mut rpos = rpos; - let chars: Vec = fn_source.chars().collect(); - while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } - } - break; - } - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) + position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 85e7f055e79..8e4149df032 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -659,6 +659,35 @@ pub fn indent_of(cx: &T, span: Span) -> Option { snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } +/// Returns the positon just before rarrow +/// +/// ```rust,ignore +/// fn into(self) -> () {} +/// ^ +/// // in case of unformatted code +/// fn into2(self)-> () {} +/// ^ +/// fn into3(self) -> () {} +/// ^ +/// ``` +#[allow(clippy::needless_pass_by_value)] +pub fn position_before_rarrow(s: String) -> Option { + s.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec = s.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }) +} + /// Extends the span to the beginning of the spans line, incl. whitespaces. /// /// ```rust,ignore From 8242b2f0a4a53f066fa579d7497ae4574e291e2e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 30 Oct 2020 18:43:27 +0100 Subject: [PATCH 0909/1110] Remove needless allow --- tests/ui/option_option.rs | 2 -- tests/ui/option_option.stderr | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index a2617a13eca..24344833641 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -72,8 +72,6 @@ mod issue_4298 { #[serde(skip_serializing_if = "Option::is_none")] #[serde(default)] #[serde(borrow)] - // FIXME: should not lint here - #[allow(clippy::option_option)] foo: Option>>, } diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 0cd4c96eb4f..8ae1d23a8e3 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -59,7 +59,7 @@ LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:77:14 + --> $DIR/option_option.rs:75:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 5253595b3bb6a3cc6502bb2327590e582d1d27e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 6 Nov 2020 19:34:34 +0100 Subject: [PATCH 0910/1110] FROM_ITER_INSTEAD_OF_COLLECT: avoid unwrapping unconditionally Fixes #6302 --- clippy_lints/src/methods/mod.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7186656f4e1..a3fa3bdd36a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3901,21 +3901,24 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); - let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); - let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap(); + if_chain! { + if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); + if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR); - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) { - // `expr` implements `FromIterator` trait - let iter_expr = snippet(cx, args[0].span, ".."); - span_lint_and_sugg( - cx, - FROM_ITER_INSTEAD_OF_COLLECT, - expr.span, - "usage of `FromIterator::from_iter`", - "use `.collect()` instead of `::from_iter()`", - format!("{}.collect()", iter_expr), - Applicability::MaybeIncorrect, - ); + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + then { + // `expr` implements `FromIterator` trait + let iter_expr = snippet(cx, args[0].span, ".."); + span_lint_and_sugg( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "usage of `FromIterator::from_iter`", + "use `.collect()` instead of `::from_iter()`", + format!("{}.collect()", iter_expr), + Applicability::MaybeIncorrect, + ); + } } } From c6a91df838b17a2366346db664072d7914ab241a Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 22 Oct 2020 23:39:25 -0700 Subject: [PATCH 0911/1110] Enable empty_loop lint for no_std crates We skip the lint if the `loop {}` is in the `#[panic_handler]` as the main recommendation we give is to panic, which obviously isn't possible in a panic handler. Signed-off-by: Joe Richey --- clippy_lints/src/loops.rs | 28 +++++++++++++--------------- clippy_lints/src/utils/mod.rs | 7 +++++++ tests/ui/empty_loop_no_std.rs | 7 ++++++- tests/ui/empty_loop_no_std.stderr | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 tests/ui/empty_loop_no_std.stderr diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 32c2562ee95..2a53210efe3 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -4,10 +4,10 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, - match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, sugg, SpanlessEq, + indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, + snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, + span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -543,17 +543,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { - // also check for empty `loop {}` statements - // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler]) - if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { - span_lint_and_help( - cx, - EMPTY_LOOP, - expr.span, - "empty `loop {}` wastes CPU cycles", - None, - "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.", - ); + // also check for empty `loop {}` statements, skipping those in #[panic_handler] + if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) { + let msg = "empty `loop {}` wastes CPU cycles"; + let help = if is_no_std_crate(cx.tcx.hir().krate()) { + "You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body." + } else { + "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body." + }; + span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); } // extract the expression from the first statement (if any) in a block diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8e4149df032..cba3a050249 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -468,6 +468,13 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool { .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id()) } +/// Returns `true` if the expression is in the program's `#[panic_handler]`. +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let parent = cx.tcx.hir().get_parent_item(e.hir_id); + let def_id = cx.tcx.hir().local_def_id(parent).to_def_id(); + Some(def_id) == cx.tcx.lang_items().panic_impl() +} + /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs index 879d1d5d916..4553d3ec505 100644 --- a/tests/ui/empty_loop_no_std.rs +++ b/tests/ui/empty_loop_no_std.rs @@ -10,13 +10,18 @@ use core::panic::PanicInfo; #[start] fn main(argc: isize, argv: *const *const u8) -> isize { + // This should trigger the lint loop {} } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { + // This should NOT trigger the lint loop {} } #[lang = "eh_personality"] -extern "C" fn eh_personality() {} +extern "C" fn eh_personality() { + // This should also trigger the lint + loop {} +} diff --git a/tests/ui/empty_loop_no_std.stderr b/tests/ui/empty_loop_no_std.stderr new file mode 100644 index 00000000000..1cb454c3bbe --- /dev/null +++ b/tests/ui/empty_loop_no_std.stderr @@ -0,0 +1,19 @@ +error: empty `loop {}` wastes CPU cycles + --> $DIR/empty_loop_no_std.rs:14:5 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + +error: empty `loop {}` wastes CPU cycles + --> $DIR/empty_loop_no_std.rs:26:5 + | +LL | loop {} + | ^^^^^^^ + | + = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + +error: aborting due to 2 previous errors + From 3579b7de24df6a617322ec07eae98618ef57e5ee Mon Sep 17 00:00:00 2001 From: Joseph Richey Date: Wed, 4 Nov 2020 03:27:30 -0800 Subject: [PATCH 0912/1110] Update clippy_lints/src/loops.rs Fix NITs Co-authored-by: Philipp Krones --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2a53210efe3..b246245af24 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -547,9 +547,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx.tcx.hir().krate()) { - "You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body." + "you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body" } else { - "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body." + "you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body" }; span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); } From 00dee9d9160e9030d387f2cef1ec2b6422478229 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 8 Nov 2020 12:40:41 +0100 Subject: [PATCH 0913/1110] Update reference files --- tests/ui/crashes/ice-360.stderr | 2 +- tests/ui/empty_loop.stderr | 6 +++--- tests/ui/empty_loop_no_std.stderr | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index bb03ce40355..0eb7bb12b35 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -19,7 +19,7 @@ LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: aborting due to 2 previous errors diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index fd3979f259a..555f3d3d884 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -5,7 +5,7 @@ LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:11:9 @@ -13,7 +13,7 @@ error: empty `loop {}` wastes CPU cycles LL | loop {} | ^^^^^^^ | - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:15:9 @@ -21,7 +21,7 @@ error: empty `loop {}` wastes CPU cycles LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ | - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: aborting due to 3 previous errors diff --git a/tests/ui/empty_loop_no_std.stderr b/tests/ui/empty_loop_no_std.stderr index 1cb454c3bbe..520248fcb68 100644 --- a/tests/ui/empty_loop_no_std.stderr +++ b/tests/ui/empty_loop_no_std.stderr @@ -5,7 +5,7 @@ LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` - = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop_no_std.rs:26:5 @@ -13,7 +13,7 @@ error: empty `loop {}` wastes CPU cycles LL | loop {} | ^^^^^^^ | - = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body error: aborting due to 2 previous errors From f1f780c9422f038ae78e72a99b1ca2a0d7b392bc Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sat, 7 Nov 2020 18:26:14 -0500 Subject: [PATCH 0914/1110] Add let_underscore_drop --- CHANGELOG.md | 1 + clippy_lints/src/let_underscore.rs | 56 +++++++++++++++++++++++++++-- clippy_lints/src/lib.rs | 3 ++ src/lintlist/mod.rs | 7 ++++ tests/ui/let_underscore_drop.rs | 19 ++++++++++ tests/ui/let_underscore_drop.stderr | 27 ++++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/ui/let_underscore_drop.rs create mode 100644 tests/ui/let_underscore_drop.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e0770a45c53..816d25bcd93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1787,6 +1787,7 @@ Released 2018-09-13 [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index ae2f6131b5b..9c7fd634547 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; +use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for `let _ = ` @@ -58,7 +58,40 @@ declare_clippy_lint! { "non-binding let on a synchronization lock" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]); +declare_clippy_lint! { + /// **What it does:** Checks for `let _ = ` + /// where expr has a type that implements `Drop` + /// + /// **Why is this bad?** This statement immediately drops the initializer + /// expression instead of extending its lifetime to the end of the scope, which + /// is often not intended. To extend the expression's lifetime to the end of the + /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to + /// explicitly drop the expression, `std::mem::drop` conveys your intention + /// better and is less error-prone. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust,ignore + /// struct Droppable; + /// impl Drop for Droppable { + /// fn drop(&mut self) {} + /// } + /// let _ = Droppable; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _droppable = Droppable; + /// ``` + pub LET_UNDERSCORE_DROP, + correctness, + "non-binding let on a type that implements `Drop`" +} + +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::MUTEX_GUARD, @@ -84,6 +117,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); + let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait| + init_ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + implements_trait(cx, inner_ty, drop_trait, &[]) + }, + + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + ); if contains_sync_guard { span_lint_and_help( cx, @@ -94,6 +136,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" ) + } else if implements_drop { + span_lint_and_help( + cx, + LET_UNDERSCORE_DROP, + local.span, + "non-binding let on a type that implements `Drop`", + None, + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`" + ) } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( cx, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1f4bed92e69..b44e28b4596 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -622,6 +622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, &let_if_seq::USELESS_LET_IF_SEQ, + &let_underscore::LET_UNDERSCORE_DROP, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, &lifetimes::EXTRA_UNUSED_LIFETIMES, @@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1809,6 +1811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bc0a0ad2b17..8b5e6cc916f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1117,6 +1117,13 @@ vec![ deprecation: None, module: "returns", }, + Lint { + name: "let_underscore_drop", + group: "correctness", + desc: "non-binding let on a type that implements `Drop`", + deprecation: None, + module: "let_underscore", + }, Lint { name: "let_underscore_lock", group: "correctness", diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs new file mode 100644 index 00000000000..98593edb9c5 --- /dev/null +++ b/tests/ui/let_underscore_drop.rs @@ -0,0 +1,19 @@ +#![warn(clippy::let_underscore_drop)] + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn main() { + let unit = (); + let boxed = Box::new(()); + let droppable = Droppable; + let optional = Some(Droppable); + + let _ = (); + let _ = Box::new(()); + let _ = Droppable; + let _ = Some(Droppable); +} diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr new file mode 100644 index 00000000000..6dc8904c4fe --- /dev/null +++ b/tests/ui/let_underscore_drop.stderr @@ -0,0 +1,27 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:16:5 + | +LL | let _ = Box::new(()); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:17:5 + | +LL | let _ = Droppable; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:18:5 + | +LL | let _ = Some(Droppable); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 3 previous errors + From 9c6a0b9c3490b321756c56d07c57f5537ea2b7fb Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 Nov 2020 07:07:49 -0500 Subject: [PATCH 0915/1110] Update references --- tests/ui/borrow_box.stderr | 11 ++- .../others.stderr | 27 +++++++- tests/ui/box_vec.stderr | 15 ++++- tests/ui/crashes/ice-4968.stderr | 11 +++ tests/ui/crashes/ice-5223.stderr | 11 +++ tests/ui/escape_analysis.stderr | 23 ++++++- tests/ui/eta.stderr | 19 +++++- tests/ui/filter_methods.stderr | 47 ++++++++++++- tests/ui/get_unwrap.stderr | 19 +++++- tests/ui/into_iter_on_ref.stderr | 11 ++- tests/ui/iter_cloned_collect.stderr | 23 ++++++- tests/ui/map_clone.stderr | 59 +++++++++++++++- tests/ui/map_collect_result_unit.stderr | 19 +++++- tests/ui/map_flatten.stderr | 43 +++++++++++- tests/ui/map_identity.stderr | 35 +++++++++- tests/ui/match_single_binding.stderr | 16 ++++- tests/ui/needless_pass_by_value.stderr | 11 ++- tests/ui/redundant_clone.stderr | 11 ++- tests/ui/reversed_empty_ranges_fixable.stderr | 11 ++- tests/ui/transmute.stderr | 43 +++++++++++- tests/ui/transmute_collection.stderr | 67 ++++++++++++++++++- tests/ui/unnecessary_clone.stderr | 11 ++- tests/ui/useless_conversion.stderr | 11 ++- 23 files changed, 533 insertions(+), 21 deletions(-) create mode 100644 tests/ui/crashes/ice-4968.stderr create mode 100644 tests/ui/crashes/ice-5223.stderr diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 3eac32815be..a40789cd426 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -22,6 +22,15 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` +error: non-binding let on a type that implements `Drop` + --> $DIR/borrow_box.rs:63:5 + | +LL | let _ = foo; + | ^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you seem to be trying to use `&Box`. Consider using just `&T` --> $DIR/borrow_box.rs:95:25 | @@ -64,5 +73,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 9a908cf30e9..976c412c7a8 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -47,6 +47,15 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here +error: non-binding let on a type that implements `Drop` + --> $DIR/others.rs:72:5 + | +LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:72:14 | @@ -95,6 +104,22 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here +error: non-binding let on a type that implements `Drop` + --> $DIR/others.rs:83:5 + | +LL | let _ = ATOMIC_TUPLE.1.into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/others.rs:85:5 + | +LL | let _ = &{ ATOMIC_TUPLE }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:87:5 | @@ -111,5 +136,5 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 14 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index fca12eddd57..a4983df1d30 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -1,3 +1,16 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/box_vec.rs:7:9 + | +LL | let _: Box<$x> = Box::new($init); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | boxit!(Vec::new(), Vec); + | ---------------------------- in this macro invocation + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: you seem to be trying to use `Box>`. Consider using just `Vec` --> $DIR/box_vec.rs:14:18 | @@ -7,5 +20,5 @@ LL | pub fn test(foo: Box>) { = note: `-D clippy::box-vec` implied by `-D warnings` = help: `Vec` is already on the heap, `Box>` makes an extra allocation. -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/crashes/ice-4968.stderr b/tests/ui/crashes/ice-4968.stderr new file mode 100644 index 00000000000..9ce39027451 --- /dev/null +++ b/tests/ui/crashes/ice-4968.stderr @@ -0,0 +1,11 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/ice-4968.rs:16:9 + | +LL | let _: Vec> = mem::transmute(slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-5223.stderr b/tests/ui/crashes/ice-5223.stderr new file mode 100644 index 00000000000..3ae2dd5f770 --- /dev/null +++ b/tests/ui/crashes/ice-5223.stderr @@ -0,0 +1,11 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/ice-5223.rs:14:9 + | +LL | let _ = self.arr.iter().cloned().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to previous error + diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/escape_analysis.stderr index c86a769a3da..6e1c1c07b6e 100644 --- a/tests/ui/escape_analysis.stderr +++ b/tests/ui/escape_analysis.stderr @@ -12,5 +12,26 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/escape_analysis.rs:166:9 + | +LL | / let _ = move || { +LL | | consume(x); +LL | | }; + | |__________^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/escape_analysis.rs:172:9 + | +LL | / let _ = || { +LL | | borrow(&x); +LL | | }; + | |__________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 4 previous errors diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index c4713ca8083..bc79caee887 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -64,6 +64,15 @@ error: redundant closure found LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` +error: non-binding let on a type that implements `Drop` + --> $DIR/eta.rs:107:5 + | +LL | let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: redundant closure found --> $DIR/eta.rs:172:27 | @@ -76,5 +85,13 @@ error: redundant closure found LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` -error: aborting due to 12 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/eta.rs:203:5 + | +LL | let _ = [Bar].iter().map(|s| s.to_string()).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 14 previous errors diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 91718dd1175..08b781d7363 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:5:5 + | +LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:5:21 | @@ -7,6 +16,18 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:7:5 + | +LL | / let _: Vec<_> = vec![5_i8; 6] +LL | | .into_iter() +LL | | .filter(|&x| x == 0) +LL | | .flat_map(|x| x.checked_mul(2)) +LL | | .collect(); + | |___________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:7:21 | @@ -19,6 +40,18 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:13:5 + | +LL | / let _: Vec<_> = vec![5_i8; 6] +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .flat_map(|x| x.checked_mul(2)) +LL | | .collect(); + | |___________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter_map(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:13:21 | @@ -31,6 +64,18 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:19:5 + | +LL | / let _: Vec<_> = vec![5_i8; 6] +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .map(|x| x.checked_mul(2)) +LL | | .collect(); + | |___________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter_map(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:19:21 | @@ -43,5 +88,5 @@ LL | | .map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by only calling `.filter_map(..)` instead -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 76a098df82a..6aa5452bac1 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -70,17 +70,34 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` +error: non-binding let on a type that implements `Drop` + --> $DIR/get_unwrap.rs:59:9 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:59:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` +error: non-binding let on a type that implements `Drop` + --> $DIR/get_unwrap.rs:60:9 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:60:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 28003b365bb..efe9d20920b 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/into_iter_on_ref.rs:13:5 + | +LL | let _ = vec![1, 2, 3].into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | @@ -162,5 +171,5 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not consume LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index b90a1e6c919..f5cd43b3da5 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -6,12 +6,33 @@ LL | let v2: Vec = v.iter().cloned().collect(); | = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/iter_cloned_collect.rs:15:5 + | +LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:15:38 | LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` +error: non-binding let on a type that implements `Drop` + --> $DIR/iter_cloned_collect.rs:19:9 + | +LL | / let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) +LL | | .to_bytes() +LL | | .iter() +LL | | .cloned() +LL | | .collect(); + | |_______________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:20:24 | @@ -22,5 +43,5 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 4f43cff5024..122a678f118 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:10:5 + | +LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | @@ -6,12 +15,28 @@ LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); | = note: `-D clippy::map-clone` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:11:5 + | +LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:12:5 + | +LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | @@ -36,5 +61,37 @@ error: you are needlessly cloning iterator elements LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call -error: aborting due to 6 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:35:9 + | +LL | let _: Vec = v.into_iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:42:9 + | +LL | let _: Vec = v.into_iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:45:9 + | +LL | let _: Vec = v.into_iter().map(|&mut x| x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:53:9 + | +LL | let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 13 previous errors diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr index 8b06e13baa6..26e876b1808 100644 --- a/tests/ui/map_collect_result_unit.stderr +++ b/tests/ui/map_collect_result_unit.stderr @@ -12,5 +12,22 @@ error: `.map().collect()` can be replaced with `.try_for_each()` LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` -error: aborting due to 2 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/map_collect_result_unit.rs:14:5 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_collect_result_unit.rs:15:5 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 4 previous errors diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index b6479cd69ea..6159b5256ee 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:14:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:14:46 | @@ -6,24 +15,56 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll | = note: `-D clippy::map-flatten` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:15:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:15:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:16:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:16:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:17:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:20:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:20:46 | @@ -36,5 +77,5 @@ error: called `map(..).flatten()` on an `Option` LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index e4a0320cbda..6bfeb186bad 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:8:5 + | +LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: unnecessary map of the identity function --> $DIR/map_identity.rs:8:47 | @@ -6,6 +15,14 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | = note: `-D clippy::map-identity` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:9:5 + | +LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: unnecessary map of the identity function --> $DIR/map_identity.rs:9:57 | @@ -33,5 +50,21 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: aborting due to 5 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:15:5 + | +LL | let _: Vec<_> = x.iter().map(|x| 2 * x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:16:5 + | +LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 9 previous errors diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 795c8c3e24d..8b07599817b 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -150,6 +150,20 @@ LL | let Point { x, y } = coords(); LL | let product = x * y; | +error: non-binding let on a type that implements `Drop` + --> $DIR/match_single_binding.rs:96:5 + | +LL | / let _ = v +LL | | .iter() +LL | | .map(|i| match i.unwrap() { +LL | | unwrapped => unwrapped, +LL | | }) +LL | | .collect::>(); + | |______________________________^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this match could be written as a `let` statement --> $DIR/match_single_binding.rs:98:18 | @@ -167,5 +181,5 @@ LL | unwrapped LL | }) | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 9aa783bf904..cf04ee4b257 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -90,6 +90,15 @@ help: change `v.clone()` to LL | let _ = v.to_owned(); | ^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/needless_pass_by_value.rs:85:5 + | +LL | let _ = v.clone(); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this argument is passed by value, but not consumed in the function body --> $DIR/needless_pass_by_value.rs:94:12 | @@ -174,5 +183,5 @@ error: this argument is passed by value, but not consumed in the function body LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` -error: aborting due to 22 previous errors +error: aborting due to 23 previous errors diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 89b39254299..270c3fac990 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -167,5 +167,14 @@ note: cloned value is neither consumed nor mutated LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -error: aborting due to 14 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/redundant_clone.rs:180:5 + | +LL | let _ = a.clone(); // OK + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 15 previous errors diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index de83c4f3d63..707a5d5032e 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -10,6 +10,15 @@ help: consider using the following if you are attempting to iterate over this ra LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/reversed_empty_ranges_fixable.rs:10:5 + | +LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this range is empty so it will yield no values --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | @@ -43,5 +52,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index ad9953d12bc..d6767dc9f15 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -24,30 +24,71 @@ error: transmute from a reference to a pointer LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:34:9 + | +LL | let _: Vec = core::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:36:9 + | +LL | let _: Vec = core::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:38:9 + | +LL | let _: Vec = std::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:40:9 + | +LL | let _: Vec = std::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:42:9 + | +LL | let _: Vec = my_transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:42:27 | @@ -154,5 +195,5 @@ error: transmute from a `&mut [u8]` to a `&mut str` LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 24 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/transmute_collection.stderr b/tests/ui/transmute_collection.stderr index ebc05c402ab..e89f6d8539f 100644 --- a/tests/ui/transmute_collection.stderr +++ b/tests/ui/transmute_collection.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:9:9 + | +LL | let _ = transmute::<_, Vec>(vec![0u8]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound --> $DIR/transmute_collection.rs:9:17 | @@ -6,18 +15,42 @@ LL | let _ = transmute::<_, Vec>(vec![0u8]); | = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:11:9 + | +LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound --> $DIR/transmute_collection.rs:11:17 | LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:14:9 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:14:17 | LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:16:9 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:16:17 | @@ -60,24 +93,56 @@ error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections: LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:34:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:34:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:35:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:35:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:37:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:37:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:38:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:38:17 | @@ -108,5 +173,5 @@ error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collect LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..1f89cb0cdef 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -24,6 +24,15 @@ error: using `.clone()` on a ref-counted pointer LL | arc_weak.clone(); | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` +error: non-binding let on a type that implements `Drop` + --> $DIR/unnecessary_clone.rs:36:5 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:36:33 | @@ -102,5 +111,5 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 11c6efb25cc..ea3e96111cb 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -52,6 +52,15 @@ error: useless conversion to the same type: `std::str::Lines` LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` +error: non-binding let on a type that implements `Drop` + --> $DIR/useless_conversion.rs:65:5 + | +LL | let _ = vec![1, 2, 3].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | @@ -70,5 +79,5 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 7c612c1c5086fb52bed1e0c8f222af109c448267 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 8 Nov 2020 16:14:38 +0100 Subject: [PATCH 0916/1110] Force contributors/reviewers to set _some_ changelog entry --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6c92e10522c..a3f114e0bb3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,8 @@ Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. -If your PR only updates to the latest nightly, you can leave the -`changelog` entry as `none`. Otherwise, please write a short comment +If your PR only includes internal changes, you can just write +`changelog: none`. Otherwise, please write a short comment explaining your change. If your PR fixes an issue, you can add "fixes #issue_number" into this @@ -28,5 +28,5 @@ Delete this line and everything above before opening your PR. --- -*Please keep the line below* -changelog: none +*Please write a short comment explaining your change (or "none" for internal only changes)* +changelog: From b0994064b03f1f9f8bdb8f594bdaec9dbaa0788a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 29 Oct 2020 16:21:01 -0500 Subject: [PATCH 0917/1110] Make KNOW_TYPES static --- clippy_lints/src/methods/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a3fa3bdd36a..5309253531b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1803,6 +1803,14 @@ fn lint_or_fun_call<'tcx>( or_has_args: bool, span: Span, ) { + // (path, fn_has_argument, methods, suffix) + static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ + (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + ]; + if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { if path.ident.as_str() == "len" { let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -1818,16 +1826,8 @@ fn lint_or_fun_call<'tcx>( } } - // (path, fn_has_argument, methods, suffix) - let know_types: &[(&[_], _, &[_], _)] = &[ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), - ]; - if_chain! { - if know_types.iter().any(|k| k.2.contains(&name)); + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); if is_lazyness_candidate(cx, arg); if !contains_return(&arg); @@ -1835,7 +1835,7 @@ fn lint_or_fun_call<'tcx>( let self_ty = cx.typeck_results().expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); + KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); if poss.contains(&name); From 9cab08465b5d5b4bb4f5ff406b2e18ca550f7d93 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 29 Oct 2020 15:30:20 -0500 Subject: [PATCH 0918/1110] Fix or_fun_call for index operator --- clippy_lints/src/methods/mod.rs | 45 +++++++++++-------------- clippy_lints/src/utils/eager_or_lazy.rs | 7 +++- tests/ui/or_fun_call.fixed | 9 +++++ tests/ui/or_fun_call.rs | 9 +++++ tests/ui/or_fun_call.stderr | 18 ++++++++-- 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5309253531b..b6fb3d06934 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1797,11 +1797,11 @@ fn lint_or_fun_call<'tcx>( cx: &LateContext<'tcx>, name: &str, method_span: Span, - fun_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - or_has_args: bool, span: Span, + // None if lambda is required + fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ @@ -1840,10 +1840,18 @@ fn lint_or_fun_call<'tcx>( if poss.contains(&name); then { - let sugg: Cow<'_, _> = match (fn_has_arguments, !or_has_args) { - (true, _) => format!("|_| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), - (false, false) => format!("|| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), - (false, true) => snippet_with_macro_callsite(cx, fun_span, ".."), + let sugg: Cow<'_, str> = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + let snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{}| {}", l_arg, snippet).into() + } else { + snippet + } }; let span_replace_word = method_span.with_hi(span.hi()); span_lint_and_sugg( @@ -1864,28 +1872,13 @@ fn lint_or_fun_call<'tcx>( hir::ExprKind::Call(ref fun, ref or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { - check_general_case( - cx, - name, - method_span, - fun.span, - &args[0], - &args[1], - or_has_args, - expr.span, - ); + let fun_span = if or_has_args { None } else { Some(fun.span) }; + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); } }, - hir::ExprKind::MethodCall(_, span, ref or_args, _) => check_general_case( - cx, - name, - method_span, - span, - &args[0], - &args[1], - !or_args.is_empty(), - expr.span, - ), + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + }, _ => {}, } } diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 4ceea13df37..8fe5ddee1ca 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::utils::is_ctor_or_promotable_const_function; +use crate::utils::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -96,6 +96,11 @@ fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, ex let call_found = match &expr.kind { // ignore enum and struct constructors ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + ExprKind::Index(obj, _) => { + let ty = self.cx.typeck_results().expr_ty(obj); + is_type_diagnostic_item(self.cx, ty, sym!(hashmap_type)) + || match_type(self.cx, ty, &paths::BTREEMAP) + }, ExprKind::MethodCall(..) => true, _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2045ffdb5f0..20e5016bc17 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -70,6 +70,15 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; let _ = opt.ok_or(format!("{} world.", hello)); + + // index + let map = HashMap::::new(); + let _ = Some(1).unwrap_or_else(|| map[&1]); + let map = BTreeMap::::new(); + let _ = Some(1).unwrap_or_else(|| map[&1]); + // don't lint index vec + let vec = vec![1]; + let _ = Some(1).unwrap_or(vec[1]); } struct Foo(u8); diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 522f31b72d0..e7192deeebb 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -70,6 +70,15 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; let _ = opt.ok_or(format!("{} world.", hello)); + + // index + let map = HashMap::::new(); + let _ = Some(1).unwrap_or(map[&1]); + let map = BTreeMap::::new(); + let _ = Some(1).unwrap_or(map[&1]); + // don't lint index vec + let vec = vec![1]; + let _ = Some(1).unwrap_or(vec[1]); } struct Foo(u8); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..d0c4df0e008 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -78,17 +78,29 @@ error: use of `unwrap_or` followed by a function call LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:76:21 + | +LL | let _ = Some(1).unwrap_or(map[&1]); + | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:78:21 + | +LL | let _ = Some(1).unwrap_or(map[&1]); + | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:102:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:106:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors From aa6bf1f90d9191cdf4c6d925f8137fa8eac37889 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius" <35515885+smoelius@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:24:55 -0500 Subject: [PATCH 0919/1110] Update clippy_lints/src/let_underscore.rs Co-authored-by: Philipp Krones --- clippy_lints/src/let_underscore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 9c7fd634547..e8e64bca535 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -141,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { cx, LET_UNDERSCORE_DROP, local.span, - "non-binding let on a type that implements `Drop`", + "non-binding `let` on a type that implements `Drop`", None, "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" From 9751cba2c675c2cd46742d2a81c891d4db4deacc Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius" <35515885+smoelius@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:25:23 -0500 Subject: [PATCH 0920/1110] Update clippy_lints/src/let_underscore.rs Co-authored-by: Philipp Krones --- clippy_lints/src/let_underscore.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index e8e64bca535..fb2df9369dc 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -79,12 +79,20 @@ declare_clippy_lint! { /// impl Drop for Droppable { /// fn drop(&mut self) {} /// } - /// let _ = Droppable; + /// { + /// let _ = Droppable; + /// // ^ dropped here + /// /* more code */ + /// } /// ``` /// /// Good: /// ```rust,ignore - /// let _droppable = Droppable; + /// { + /// let _droppable = Droppable; + /// /* more code */ + /// // dropped at end of scope + /// } /// ``` pub LET_UNDERSCORE_DROP, correctness, From 8211b597baff7d599df8c1fe42f4e7d15b7d3e95 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius" <35515885+smoelius@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:25:50 -0500 Subject: [PATCH 0921/1110] Update clippy_lints/src/let_underscore.rs Co-authored-by: Philipp Krones --- clippy_lints/src/let_underscore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index fb2df9369dc..6a5a77f8690 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -95,7 +95,7 @@ declare_clippy_lint! { /// } /// ``` pub LET_UNDERSCORE_DROP, - correctness, + pedantic, "non-binding let on a type that implements `Drop`" } From 40d7af50ed9811e427aa65b0111bca261ecf1239 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 Nov 2020 17:33:54 -0500 Subject: [PATCH 0922/1110] Update lints --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b44e28b4596..20b38cbb6d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1241,6 +1241,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), @@ -1384,7 +1385,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1811,7 +1811,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8b5e6cc916f..4f1b56ed9be 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1119,7 +1119,7 @@ vec![ }, Lint { name: "let_underscore_drop", - group: "correctness", + group: "pedantic", desc: "non-binding let on a type that implements `Drop`", deprecation: None, module: "let_underscore", From 06e81bb49370a0154c3fa7de0bbc8b31c7bbe37b Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 Nov 2020 18:32:12 -0500 Subject: [PATCH 0923/1110] Update references --- tests/ui/borrow_box.stderr | 11 +-- .../others.stderr | 27 +------ tests/ui/box_vec.stderr | 15 +--- tests/ui/crashes/ice-4968.stderr | 11 --- tests/ui/crashes/ice-5223.stderr | 11 --- tests/ui/escape_analysis.stderr | 23 +----- tests/ui/eta.stderr | 19 +---- tests/ui/filter_methods.stderr | 8 +-- tests/ui/get_unwrap.stderr | 19 +---- tests/ui/into_iter_on_ref.stderr | 11 +-- tests/ui/iter_cloned_collect.stderr | 23 +----- tests/ui/let_underscore_drop.stderr | 6 +- tests/ui/map_clone.fixed | 1 + tests/ui/map_clone.rs | 1 + tests/ui/map_clone.stderr | 71 ++----------------- tests/ui/map_collect_result_unit.stderr | 19 +---- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/map_flatten.stderr | 55 ++------------ tests/ui/map_identity.stderr | 35 +-------- tests/ui/match_single_binding.stderr | 16 +---- tests/ui/needless_pass_by_value.stderr | 11 +-- tests/ui/redundant_clone.stderr | 11 +-- tests/ui/reversed_empty_ranges_fixable.stderr | 11 +-- tests/ui/transmute.stderr | 43 +---------- tests/ui/transmute_collection.stderr | 67 +---------------- tests/ui/unnecessary_clone.stderr | 11 +-- tests/ui/useless_conversion.stderr | 11 +-- 28 files changed, 43 insertions(+), 506 deletions(-) delete mode 100644 tests/ui/crashes/ice-4968.stderr delete mode 100644 tests/ui/crashes/ice-5223.stderr diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index a40789cd426..3eac32815be 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -22,15 +22,6 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` -error: non-binding let on a type that implements `Drop` - --> $DIR/borrow_box.rs:63:5 - | -LL | let _ = foo; - | ^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you seem to be trying to use `&Box`. Consider using just `&T` --> $DIR/borrow_box.rs:95:25 | @@ -73,5 +64,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 976c412c7a8..9a908cf30e9 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -47,15 +47,6 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: non-binding let on a type that implements `Drop` - --> $DIR/others.rs:72:5 - | -LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:72:14 | @@ -104,22 +95,6 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: non-binding let on a type that implements `Drop` - --> $DIR/others.rs:83:5 - | -LL | let _ = ATOMIC_TUPLE.1.into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/others.rs:85:5 - | -LL | let _ = &{ ATOMIC_TUPLE }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:87:5 | @@ -136,5 +111,5 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 17 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index a4983df1d30..fca12eddd57 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -1,16 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/box_vec.rs:7:9 - | -LL | let _: Box<$x> = Box::new($init); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | boxit!(Vec::new(), Vec); - | ---------------------------- in this macro invocation - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - error: you seem to be trying to use `Box>`. Consider using just `Vec` --> $DIR/box_vec.rs:14:18 | @@ -20,5 +7,5 @@ LL | pub fn test(foo: Box>) { = note: `-D clippy::box-vec` implied by `-D warnings` = help: `Vec` is already on the heap, `Box>` makes an extra allocation. -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/crashes/ice-4968.stderr b/tests/ui/crashes/ice-4968.stderr deleted file mode 100644 index 9ce39027451..00000000000 --- a/tests/ui/crashes/ice-4968.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/ice-4968.rs:16:9 - | -LL | let _: Vec> = mem::transmute(slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to previous error - diff --git a/tests/ui/crashes/ice-5223.stderr b/tests/ui/crashes/ice-5223.stderr deleted file mode 100644 index 3ae2dd5f770..00000000000 --- a/tests/ui/crashes/ice-5223.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/ice-5223.rs:14:9 - | -LL | let _ = self.arr.iter().cloned().collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to previous error - diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/escape_analysis.stderr index 6e1c1c07b6e..c86a769a3da 100644 --- a/tests/ui/escape_analysis.stderr +++ b/tests/ui/escape_analysis.stderr @@ -12,26 +12,5 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/escape_analysis.rs:166:9 - | -LL | / let _ = move || { -LL | | consume(x); -LL | | }; - | |__________^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/escape_analysis.rs:172:9 - | -LL | / let _ = || { -LL | | borrow(&x); -LL | | }; - | |__________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index bc79caee887..c4713ca8083 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -64,15 +64,6 @@ error: redundant closure found LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` -error: non-binding let on a type that implements `Drop` - --> $DIR/eta.rs:107:5 - | -LL | let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: redundant closure found --> $DIR/eta.rs:172:27 | @@ -85,13 +76,5 @@ error: redundant closure found LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` -error: non-binding let on a type that implements `Drop` - --> $DIR/eta.rs:203:5 - | -LL | let _ = [Bar].iter().map(|s| s.to_string()).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 08b781d7363..b5ac90282dc 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:5:5 | LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); @@ -16,7 +16,7 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:7:5 | LL | / let _: Vec<_> = vec![5_i8; 6] @@ -40,7 +40,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:13:5 | LL | / let _: Vec<_> = vec![5_i8; 6] @@ -64,7 +64,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:19:5 | LL | / let _: Vec<_> = vec![5_i8; 6] diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 6aa5452bac1..76a098df82a 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -70,34 +70,17 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: non-binding let on a type that implements `Drop` - --> $DIR/get_unwrap.rs:59:9 - | -LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:59:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: non-binding let on a type that implements `Drop` - --> $DIR/get_unwrap.rs:60:9 - | -LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:60:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index efe9d20920b..28003b365bb 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,12 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/into_iter_on_ref.rs:13:5 - | -LL | let _ = vec![1, 2, 3].into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | @@ -171,5 +162,5 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not consume LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index f5cd43b3da5..b90a1e6c919 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -6,33 +6,12 @@ LL | let v2: Vec = v.iter().cloned().collect(); | = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/iter_cloned_collect.rs:15:5 - | -LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:15:38 | LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` -error: non-binding let on a type that implements `Drop` - --> $DIR/iter_cloned_collect.rs:19:9 - | -LL | / let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) -LL | | .to_bytes() -LL | | .iter() -LL | | .cloned() -LL | | .collect(); - | |_______________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:20:24 | @@ -43,5 +22,5 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr index 6dc8904c4fe..66069e0c5e1 100644 --- a/tests/ui/let_underscore_drop.stderr +++ b/tests/ui/let_underscore_drop.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/let_underscore_drop.rs:16:5 | LL | let _ = Box::new(()); @@ -7,7 +7,7 @@ LL | let _ = Box::new(()); = note: `-D clippy::let-underscore-drop` implied by `-D warnings` = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/let_underscore_drop.rs:17:5 | LL | let _ = Droppable; @@ -15,7 +15,7 @@ LL | let _ = Droppable; | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/let_underscore_drop.rs:18:5 | LL | let _ = Some(Droppable); diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 6e3a8e67e81..ce92b3c0c30 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -2,6 +2,7 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::iter_cloned_collect)] #![allow(clippy::clone_on_copy, clippy::redundant_clone)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::redundant_closure_for_method_calls)] #![allow(clippy::many_single_char_names)] diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 6fd395710d4..324c776c3c9 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -2,6 +2,7 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::iter_cloned_collect)] #![allow(clippy::clone_on_copy, clippy::redundant_clone)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::redundant_closure_for_method_calls)] #![allow(clippy::many_single_char_names)] diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 122a678f118..d84a5bf8d4d 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,97 +1,40 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:10:5 - | -LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:10:22 + --> $DIR/map_clone.rs:11:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:11:5 - | -LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:11:26 + --> $DIR/map_clone.rs:12:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:12:5 - | -LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:12:23 + --> $DIR/map_clone.rs:13:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:14:26 + --> $DIR/map_clone.rs:15:26 | LL | let _: Option = Some(&16).map(|b| *b); | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:15:25 + --> $DIR/map_clone.rs:16:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` error: you are needlessly cloning iterator elements - --> $DIR/map_clone.rs:26:29 + --> $DIR/map_clone.rs:27:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:35:9 - | -LL | let _: Vec = v.into_iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:42:9 - | -LL | let _: Vec = v.into_iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:45:9 - | -LL | let _: Vec = v.into_iter().map(|&mut x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:53:9 - | -LL | let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 13 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr index 26e876b1808..8b06e13baa6 100644 --- a/tests/ui/map_collect_result_unit.stderr +++ b/tests/ui/map_collect_result_unit.stderr @@ -12,22 +12,5 @@ error: `.map().collect()` can be replaced with `.try_for_each()` LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_collect_result_unit.rs:14:5 - | -LL | let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_collect_result_unit.rs:15:5 - | -LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index a5fdf7df613..a7ab5a12cb7 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index abbc4e16e56..e364a05f376 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 6159b5256ee..d4e27f9aa07 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,81 +1,40 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:14:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:14:46 + --> $DIR/map_flatten.rs:15:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` | = note: `-D clippy::map-flatten` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:15:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:15:46 + --> $DIR/map_flatten.rs:16:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:16:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:16:46 + --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:17:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:17:46 + --> $DIR/map_flatten.rs:18:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:20:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:20:46 + --> $DIR/map_flatten.rs:21:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:23:39 + --> $DIR/map_flatten.rs:24:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 11 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index 6bfeb186bad..e4a0320cbda 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -1,12 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:8:5 - | -LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: unnecessary map of the identity function --> $DIR/map_identity.rs:8:47 | @@ -15,14 +6,6 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | = note: `-D clippy::map-identity` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:9:5 - | -LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: unnecessary map of the identity function --> $DIR/map_identity.rs:9:57 | @@ -50,21 +33,5 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:15:5 - | -LL | let _: Vec<_> = x.iter().map(|x| 2 * x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:16:5 - | -LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 9 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 8b07599817b..795c8c3e24d 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -150,20 +150,6 @@ LL | let Point { x, y } = coords(); LL | let product = x * y; | -error: non-binding let on a type that implements `Drop` - --> $DIR/match_single_binding.rs:96:5 - | -LL | / let _ = v -LL | | .iter() -LL | | .map(|i| match i.unwrap() { -LL | | unwrapped => unwrapped, -LL | | }) -LL | | .collect::>(); - | |______________________________^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this match could be written as a `let` statement --> $DIR/match_single_binding.rs:98:18 | @@ -181,5 +167,5 @@ LL | unwrapped LL | }) | -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index cf04ee4b257..9aa783bf904 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -90,15 +90,6 @@ help: change `v.clone()` to LL | let _ = v.to_owned(); | ^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/needless_pass_by_value.rs:85:5 - | -LL | let _ = v.clone(); - | ^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this argument is passed by value, but not consumed in the function body --> $DIR/needless_pass_by_value.rs:94:12 | @@ -183,5 +174,5 @@ error: this argument is passed by value, but not consumed in the function body LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` -error: aborting due to 23 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 270c3fac990..89b39254299 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -167,14 +167,5 @@ note: cloned value is neither consumed nor mutated LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/redundant_clone.rs:180:5 - | -LL | let _ = a.clone(); // OK - | ^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 707a5d5032e..de83c4f3d63 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -10,15 +10,6 @@ help: consider using the following if you are attempting to iterate over this ra LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/reversed_empty_ranges_fixable.rs:10:5 - | -LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this range is empty so it will yield no values --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | @@ -52,5 +43,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index d6767dc9f15..ad9953d12bc 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -24,71 +24,30 @@ error: transmute from a reference to a pointer LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:34:9 - | -LL | let _: Vec = core::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:36:9 - | -LL | let _: Vec = core::mem::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:38:9 - | -LL | let _: Vec = std::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:40:9 - | -LL | let _: Vec = std::mem::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:42:9 - | -LL | let _: Vec = my_transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:42:27 | @@ -195,5 +154,5 @@ error: transmute from a `&mut [u8]` to a `&mut str` LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 29 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/transmute_collection.stderr b/tests/ui/transmute_collection.stderr index e89f6d8539f..ebc05c402ab 100644 --- a/tests/ui/transmute_collection.stderr +++ b/tests/ui/transmute_collection.stderr @@ -1,12 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:9:9 - | -LL | let _ = transmute::<_, Vec>(vec![0u8]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound --> $DIR/transmute_collection.rs:9:17 | @@ -15,42 +6,18 @@ LL | let _ = transmute::<_, Vec>(vec![0u8]); | = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:11:9 - | -LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound --> $DIR/transmute_collection.rs:11:17 | LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:14:9 - | -LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:14:17 | LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:16:9 - | -LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:16:17 | @@ -93,56 +60,24 @@ error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections: LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:34:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:34:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:35:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:35:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:37:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:37:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:38:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:38:17 | @@ -173,5 +108,5 @@ error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collect LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 1f89cb0cdef..5ffa6c4fd06 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -24,15 +24,6 @@ error: using `.clone()` on a ref-counted pointer LL | arc_weak.clone(); | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` -error: non-binding let on a type that implements `Drop` - --> $DIR/unnecessary_clone.rs:36:5 - | -LL | let _: Arc = x.clone(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:36:33 | @@ -111,5 +102,5 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index ea3e96111cb..11c6efb25cc 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -52,15 +52,6 @@ error: useless conversion to the same type: `std::str::Lines` LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: non-binding let on a type that implements `Drop` - --> $DIR/useless_conversion.rs:65:5 - | -LL | let _ = vec![1, 2, 3].into_iter().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | @@ -79,5 +70,5 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors From 4852cca61bb989adf77b1d202eae6b40067fa9ab Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Mon, 9 Nov 2020 07:49:14 -0500 Subject: [PATCH 0924/1110] Allow `let_underscore_drop` in `filter_methods` test --- tests/ui/filter_methods.rs | 1 + tests/ui/filter_methods.stderr | 55 ++++------------------------------ 2 files changed, 6 insertions(+), 50 deletions(-) diff --git a/tests/ui/filter_methods.rs b/tests/ui/filter_methods.rs index ef434245fd7..51450241619 100644 --- a/tests/ui/filter_methods.rs +++ b/tests/ui/filter_methods.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] fn main() { diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index b5ac90282dc..11922681379 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,14 +1,5 @@ -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:5:5 - | -LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:5:21 + --> $DIR/filter_methods.rs:6:21 | LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,20 +7,8 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:7:5 - | -LL | / let _: Vec<_> = vec![5_i8; 6] -LL | | .into_iter() -LL | | .filter(|&x| x == 0) -LL | | .flat_map(|x| x.checked_mul(2)) -LL | | .collect(); - | |___________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:7:21 + --> $DIR/filter_methods.rs:8:21 | LL | let _: Vec<_> = vec![5_i8; 6] | _____________________^ @@ -40,20 +19,8 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:13:5 - | -LL | / let _: Vec<_> = vec![5_i8; 6] -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .flat_map(|x| x.checked_mul(2)) -LL | | .collect(); - | |___________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter_map(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:13:21 + --> $DIR/filter_methods.rs:14:21 | LL | let _: Vec<_> = vec![5_i8; 6] | _____________________^ @@ -64,20 +31,8 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:19:5 - | -LL | / let _: Vec<_> = vec![5_i8; 6] -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .map(|x| x.checked_mul(2)) -LL | | .collect(); - | |___________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter_map(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:19:21 + --> $DIR/filter_methods.rs:20:21 | LL | let _: Vec<_> = vec![5_i8; 6] | _____________________^ @@ -88,5 +43,5 @@ LL | | .map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by only calling `.filter_map(..)` instead -error: aborting due to 8 previous errors +error: aborting due to 4 previous errors From faa3e233169523f6bb1537d9b6b2aabe66efd01b Mon Sep 17 00:00:00 2001 From: chansuke Date: Tue, 3 Nov 2020 09:00:30 +0900 Subject: [PATCH 0925/1110] Add exteranal macros for as_conversions --- tests/ui/as_conversions.rs | 14 +++++++++++++- tests/ui/as_conversions.stderr | 6 +++--- tests/ui/auxiliary/macro_rules.rs | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/ui/as_conversions.rs b/tests/ui/as_conversions.rs index e01ba0c64df..cd745feec6d 100644 --- a/tests/ui/as_conversions.rs +++ b/tests/ui/as_conversions.rs @@ -1,7 +1,19 @@ -#[warn(clippy::as_conversions)] +// aux-build:macro_rules.rs + +#![warn(clippy::as_conversions)] + +#[macro_use] +extern crate macro_rules; + +fn with_external_macro() { + as_conv_with_arg!(0u32 as u64); + as_conv!(); +} fn main() { let i = 0u32 as u64; let j = &i as *const u64 as *mut u64; + + with_external_macro(); } diff --git a/tests/ui/as_conversions.stderr b/tests/ui/as_conversions.stderr index 312d3a7460e..f5f75d3aee0 100644 --- a/tests/ui/as_conversions.stderr +++ b/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:4:13 + --> $DIR/as_conversions.rs:14:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:6:13 + --> $DIR/as_conversions.rs:16:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:6:13 + --> $DIR/as_conversions.rs:16:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 93303865e17..f985a15eda2 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -70,3 +70,17 @@ macro_rules! ref_arg_function { fn fun_example(ref _x: usize) {} }; } + +#[macro_export] +macro_rules! as_conv_with_arg { + (0u32 as u64) => { + () + }; +} + +#[macro_export] +macro_rules! as_conv { + () => { + 0u32 as u64 + }; +} From effcb52bbf818ca39c57f9beb7d7d63b1e47d1fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 10 Nov 2020 22:33:02 +0100 Subject: [PATCH 0926/1110] Run cargo dev fmt --- clippy_lints/src/non_expressive_names.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 6b175490cc8..5b42b61fcde 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,7 +1,5 @@ use crate::utils::{span_lint, span_lint_and_then}; -use rustc_ast::ast::{ - Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, -}; +use rustc_ast::ast::{Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind}; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; From 769094410a22849c426972dd421a1937463a48e7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 09:18:16 -0500 Subject: [PATCH 0927/1110] Fix map_clone with deref and clone --- clippy_lints/src/map_clone.rs | 14 +++++++++----- tests/ui/map_clone.fixed | 7 +++++++ tests/ui/map_clone.rs | 7 +++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 9a00608ce39..3f34dd5112e 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -8,6 +8,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; +use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; @@ -75,11 +76,14 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } } }, - hir::ExprKind::MethodCall(ref method, _, ref obj, _) => { - if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone" - && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { - - let obj_ty = cx.typeck_results().expr_ty(&obj[0]); + hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { + if ident_eq(name, obj) && method.ident.name == sym::clone; + if match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT); + // no autoderefs + if !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); + then { + let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { if matches!(mutability, Mutability::Not) { let copy = is_copy(cx, ty); diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 6e3a8e67e81..504ae281fe7 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -52,4 +52,11 @@ fn main() { let items = vec![&mut aa, &mut bb]; let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); } + + // Issue #6239 deref coercion and clone deref + { + use std::cell::RefCell; + + let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 6fd395710d4..9348e6bae7a 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -52,4 +52,11 @@ fn main() { let items = vec![&mut aa, &mut bb]; let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); } + + // Issue #6239 deref coercion and clone deref + { + use std::cell::RefCell; + + let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); + } } From a1cf2d334d685fa11fdc96fc98f35292254e5651 Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Mon, 5 Oct 2020 21:23:36 -0700 Subject: [PATCH 0928/1110] Added a lint as suggested in 6010 which recommends using `contains()` instead of `find()` follows by `is_some()` on strings Update clippy_lints/src/find_is_some_on_strs.rs Co-authored-by: Takayuki Nakata Update clippy_lints/src/methods/mod.rs Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 38 ++++++++++++++++++++++++++++++--- src/lintlist/mod.rs | 2 +- tests/ui/methods.rs | 21 +++++++++++++++++- tests/ui/methods.stderr | 2 -- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6fb3d06934..bd04a95e4a1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -515,11 +515,11 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for an iterator search (such as `find()`, + /// **What it does:** Checks for an iterator or string search (such as `find()`, /// `position()`, or `rposition()`) followed by a call to `is_some()`. /// /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.any(_)`. + /// `_.any(_)` or `_.contains(_)`. /// /// **Known problems:** None. /// @@ -535,7 +535,7 @@ declare_clippy_lint! { /// ``` pub SEARCH_IS_SOME, complexity, - "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`" + "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`" } declare_clippy_lint! { @@ -3041,6 +3041,7 @@ fn lint_flat_map_identity<'tcx>( } /// lint searching an Iterator followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` fn lint_search_is_some<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -3094,6 +3095,37 @@ fn lint_search_is_some<'tcx>( span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); } } + // lint if `find()` is called by `String` or `&str` + else if search_method == "find" { + let is_string_or_str_slice = |e| { + let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); + if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + true + } else { + *self_ty.kind() == ty::Str + } + }; + if_chain! { + if is_string_or_str_slice(&search_args[0]); + if is_string_or_str_slice(&search_args[1]); + then { + let msg = "called `is_some()` after calling `find()` \ + on a string. This is more succinctly expressed by calling \ + `contains()`.".to_string(); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "try this", + format!("contains({})", find_arg), + applicability, + ); + } + } + } } /// Used for `lint_binary_expr_with_method_call`. diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4f1b56ed9be..69acd3d9b8b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2121,7 +2121,7 @@ vec![ Lint { name: "search_is_some", group: "complexity", - desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`", + desc: "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`", deprecation: None, module: "methods", }, diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index d93e5b114ec..92ec00a11d2 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -168,8 +168,27 @@ fn search_is_some() { x < 0 } ).is_some(); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // Check caller `find()` is a &`static str case + let _ = "hello world".find("world").is_some(); + let _ = "hello world".find(&s2).is_some(); + let _ = "hello world".find(&s2[2..]).is_some(); + // Check caller of `find()` is a String case + let _ = s1.find("world").is_some(); + let _ = s1.find(&s2).is_some(); + let _ = s1.find(&s2[2..]).is_some(); + // Check caller of `find()` is a slice of String case + let _ = s1[2..].find("world").is_some(); + let _ = s1[2..].find(&s2).is_some(); + let _ = s1[2..].find(&s2[2..]).is_some(); - // Check that we don't lint if the caller is not an `Iterator`. + // Check that we don't lint if `find()` is called with + // Pattern that is not a string + let _ = s1.find(|c: char| c == 'o' || c == 'l').is_some(); + + // Check that we don't lint if the caller is not an `Iterator` or string let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.find().is_some(); let _ = foo.position().is_some(); diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 8a281c2dbd2..b2b551bd5f8 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -88,5 +88,3 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 11 previous errors - From 431fcbcc00eb4634178406c1afdf955e5b3be07a Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 17 Oct 2020 14:07:22 -0700 Subject: [PATCH 0929/1110] Moved the tests for lint `search_is_some` to new files `search_is_some.rs` and `search_is_some_fixable.rs` --- tests/ui/methods.rs | 63 ------------------------------ tests/ui/search_is_some.rs | 39 ++++++++++++++++++ tests/ui/search_is_some_fixable.rs | 35 +++++++++++++++++ 3 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 tests/ui/search_is_some.rs create mode 100644 tests/ui/search_is_some_fixable.rs diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 92ec00a11d2..513d930e056 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -133,69 +133,6 @@ fn filter_next() { let _ = foo.filter().next(); } -/// Checks implementation of `SEARCH_IS_SOME` lint. -#[rustfmt::skip] -fn search_is_some() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - let y = &&42; - - // Check `find().is_some()`, single-line case. - let _ = v.iter().find(|&x| *x < 0).is_some(); - let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - let _ = (0..1).find(|x| *x == 0).is_some(); - let _ = v.iter().find(|x| **x == 0).is_some(); - - // Check `find().is_some()`, multi-line case. - let _ = v.iter().find(|&x| { - *x < 0 - } - ).is_some(); - - // Check `position().is_some()`, single-line case. - let _ = v.iter().position(|&x| x < 0).is_some(); - - // Check `position().is_some()`, multi-line case. - let _ = v.iter().position(|&x| { - x < 0 - } - ).is_some(); - - // Check `rposition().is_some()`, single-line case. - let _ = v.iter().rposition(|&x| x < 0).is_some(); - - // Check `rposition().is_some()`, multi-line case. - let _ = v.iter().rposition(|&x| { - x < 0 - } - ).is_some(); - - let s1 = String::from("hello world"); - let s2 = String::from("world"); - // Check caller `find()` is a &`static str case - let _ = "hello world".find("world").is_some(); - let _ = "hello world".find(&s2).is_some(); - let _ = "hello world".find(&s2[2..]).is_some(); - // Check caller of `find()` is a String case - let _ = s1.find("world").is_some(); - let _ = s1.find(&s2).is_some(); - let _ = s1.find(&s2[2..]).is_some(); - // Check caller of `find()` is a slice of String case - let _ = s1[2..].find("world").is_some(); - let _ = s1[2..].find(&s2).is_some(); - let _ = s1[2..].find(&s2[2..]).is_some(); - - // Check that we don't lint if `find()` is called with - // Pattern that is not a string - let _ = s1.find(|c: char| c == 'o' || c == 'l').is_some(); - - // Check that we don't lint if the caller is not an `Iterator` or string - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.find().is_some(); - let _ = foo.position().is_some(); - let _ = foo.rposition().is_some(); -} - fn main() { filter_next(); - search_is_some(); } diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs new file mode 100644 index 00000000000..1ce372ab1d3 --- /dev/null +++ b/tests/ui/search_is_some.rs @@ -0,0 +1,39 @@ +#[macro_use] +extern crate option_helpers; +use option_helpers::IteratorFalsePositives; + +#[warn(clippy::search_is_some)] +#[rustfmt::skip] +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + + // Check `find().is_some()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_some(); + + // Check `position().is_some()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_some(); + + // Check `rposition().is_some()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_some(); + + // Check that we don't lint if the caller is not an `Iterator` or string + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.find().is_some(); + let _ = foo.position().is_some(); + let _ = foo.rposition().is_some(); + // check that we don't lint if `find()` is called with + // `Pattern` that is not a string + let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); +} + diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs new file mode 100644 index 00000000000..5bffb7e849f --- /dev/null +++ b/tests/ui/search_is_some_fixable.rs @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::search_is_some)] + +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_some()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_some(); + let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_some(); + let _ = v.iter().find(|x| **x == 0).is_some(); + + // Check `position().is_some()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_some(); + + // Check `rposition().is_some()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_some(); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // caller of `find()` is a `&`static str` + let _ = "hello world".find("world").is_some(); + let _ = "hello world".find(&s2).is_some(); + let _ = "hello world".find(&s2[2..]).is_some(); + // caller of `find()` is a `String` + let _ = s1.find("world").is_some(); + let _ = s1.find(&s2).is_some(); + let _ = s1.find(&s2[2..]).is_some(); + // caller of `find()` is slice of `String` + let _ = s1[2..].find("world").is_some(); + let _ = s1[2..].find(&s2).is_some(); + let _ = s1[2..].find(&s2[2..]).is_some(); +} From 55dc822062eb760afff0d242dea193aabc2c9771 Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 17 Oct 2020 14:28:00 -0700 Subject: [PATCH 0930/1110] Ran `tests/ui/update-all-references.sh" and `cargo dev fmt` --- tests/ui/methods.stderr | 68 ------------------- tests/ui/search_is_some.rs | 3 +- tests/ui/search_is_some.stderr | 42 ++++++++++++ tests/ui/search_is_some_fixable.fixed | 35 ++++++++++ tests/ui/search_is_some_fixable.rs | 6 +- tests/ui/search_is_some_fixable.stderr | 94 ++++++++++++++++++++++++++ 6 files changed, 175 insertions(+), 73 deletions(-) create mode 100644 tests/ui/search_is_some.stderr create mode 100644 tests/ui/search_is_some_fixable.fixed create mode 100644 tests/ui/search_is_some_fixable.stderr diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b2b551bd5f8..bf4675966df 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -20,71 +20,3 @@ LL | | ).next(); | = note: `-D clippy::filter-next` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:143:22 - | -LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` - | - = note: `-D clippy::search-is-some` implied by `-D warnings` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:144:20 - | -LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:145:20 - | -LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:146:22 - | -LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:149:13 - | -LL | let _ = v.iter().find(|&x| { - | _____________^ -LL | | *x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:155:22 - | -LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` - -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:158:13 - | -LL | let _ = v.iter().position(|&x| { - | _____________^ -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:164:22 - | -LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` - -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:167:13 - | -LL | let _ = v.iter().rposition(|&x| { - | _____________^ -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 1ce372ab1d3..1399138a0d2 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -1,4 +1,4 @@ -#[macro_use] +// aux-build:option_helpers.rs extern crate option_helpers; use option_helpers::IteratorFalsePositives; @@ -36,4 +36,3 @@ fn main() { // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); } - diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr new file mode 100644 index 00000000000..a124ab1dfd4 --- /dev/null +++ b/tests/ui/search_is_some.stderr @@ -0,0 +1,42 @@ +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some.rs:13:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some.rs:19:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some.rs:25:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/search_is_some.rs:31:9 + | +LL | let foo = IteratorFalsePositives { foo: 0 }; + | ^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/search_is_some_fixable.fixed b/tests/ui/search_is_some_fixable.fixed new file mode 100644 index 00000000000..dc3f290e562 --- /dev/null +++ b/tests/ui/search_is_some_fixable.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::search_is_some)] + +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_some()`, single-line case. + let _ = v.iter().any(|x| *x < 0); + let _ = (0..1).any(|x| **y == x); // one dereference less + let _ = (0..1).any(|x| x == 0); + let _ = v.iter().any(|x| *x == 0); + + // Check `position().is_some()`, single-line case. + let _ = v.iter().any(|&x| x < 0); + + // Check `rposition().is_some()`, single-line case. + let _ = v.iter().any(|&x| x < 0); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // caller of `find()` is a `&`static str` + let _ = "hello world".contains("world"); + let _ = "hello world".contains(&s2); + let _ = "hello world".contains(&s2[2..]); + // caller of `find()` is a `String` + let _ = s1.contains("world"); + let _ = s1.contains(&s2); + let _ = s1.contains(&s2[2..]); + // caller of `find()` is slice of `String` + let _ = s1[2..].contains("world"); + let _ = s1[2..].contains(&s2); + let _ = s1[2..].contains(&s2[2..]); +} diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs index 5bffb7e849f..146cf5adf1b 100644 --- a/tests/ui/search_is_some_fixable.rs +++ b/tests/ui/search_is_some_fixable.rs @@ -5,16 +5,16 @@ fn main() { let v = vec![3, 2, 1, 0, -1, -2, -3]; let y = &&42; - + // Check `find().is_some()`, single-line case. let _ = v.iter().find(|&x| *x < 0).is_some(); let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less let _ = (0..1).find(|x| *x == 0).is_some(); let _ = v.iter().find(|x| **x == 0).is_some(); - + // Check `position().is_some()`, single-line case. let _ = v.iter().position(|&x| x < 0).is_some(); - + // Check `rposition().is_some()`, single-line case. let _ = v.iter().rposition(|&x| x < 0).is_some(); diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr new file mode 100644 index 00000000000..7a2c063fee8 --- /dev/null +++ b/tests/ui/search_is_some_fixable.stderr @@ -0,0 +1,94 @@ +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:10:22 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:11:20 + | +LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:12:20 + | +LL | let _ = (0..1).find(|x| *x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:13:22 + | +LL | let _ = v.iter().find(|x| **x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:16:22 + | +LL | let _ = v.iter().position(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:19:22 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:24:27 + | +LL | let _ = "hello world".find("world").is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:25:27 + | +LL | let _ = "hello world".find(&s2).is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:26:27 + | +LL | let _ = "hello world".find(&s2[2..]).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:28:16 + | +LL | let _ = s1.find("world").is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:29:16 + | +LL | let _ = s1.find(&s2).is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:30:16 + | +LL | let _ = s1.find(&s2[2..]).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:32:21 + | +LL | let _ = s1[2..].find("world").is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:33:21 + | +LL | let _ = s1[2..].find(&s2).is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:34:21 + | +LL | let _ = s1[2..].find(&s2[2..]).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + +error: aborting due to 15 previous errors + From e9612f3eca5566b601dda60c999be69e50f89cac Mon Sep 17 00:00:00 2001 From: rsulli55 Date: Sun, 25 Oct 2020 18:11:38 -0700 Subject: [PATCH 0931/1110] Remove `to_string` on msg Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bd04a95e4a1..4df1b3d197f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3111,7 +3111,7 @@ fn lint_search_is_some<'tcx>( then { let msg = "called `is_some()` after calling `find()` \ on a string. This is more succinctly expressed by calling \ - `contains()`.".to_string(); + `contains()`"; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( From fb74b4802eda6da49f0ea9f2bc1ef238306c1329 Mon Sep 17 00:00:00 2001 From: rsulli55 Date: Sun, 25 Oct 2020 18:11:57 -0700 Subject: [PATCH 0932/1110] Remove borrow Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4df1b3d197f..fc66bf0422f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3118,7 +3118,7 @@ fn lint_search_is_some<'tcx>( cx, SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), - &msg, + msg, "try this", format!("contains({})", find_arg), applicability, From ee1b959054aaf69968d440915766e834568de8fd Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 31 Oct 2020 12:40:56 -0700 Subject: [PATCH 0933/1110] Added period back to lint `search_is_some` and ran `update-all-references.sh` --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/methods.stderr | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc66bf0422f..19e63fbddf7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3111,7 +3111,7 @@ fn lint_search_is_some<'tcx>( then { let msg = "called `is_some()` after calling `find()` \ on a string. This is more succinctly expressed by calling \ - `contains()`"; + `contains()`."; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index bf4675966df..33aba630a53 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -20,3 +20,5 @@ LL | | ).next(); | = note: `-D clippy::filter-next` implied by `-D warnings` +error: aborting due to 2 previous errors + From fd303132a27f0fa9bbbfb3282200f8190353574b Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 7 Nov 2020 00:21:22 -0700 Subject: [PATCH 0934/1110] Cleaned up message and suggestion for `lint_search_is_some` --- clippy_lints/src/methods/mod.rs | 14 +++--- tests/ui/search_is_some.stderr | 11 +++-- tests/ui/search_is_some_fixable.stderr | 60 +++++++++++++------------- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 19e63fbddf7..66f5aa0c6a0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3053,10 +3053,10 @@ fn lint_search_is_some<'tcx>( // lint if caller of search is an Iterator if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { let msg = format!( - "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \ - expressed by calling `any()`.", + "called `is_some()` after searching an `Iterator` with {}", search_method ); + let hint = "this is more succinctly expressed by calling `any()`"; let search_snippet = snippet(cx, search_args[1].span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -3084,7 +3084,7 @@ fn lint_search_is_some<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "try this", + "use `any()` instead", format!( "any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -3092,7 +3092,7 @@ fn lint_search_is_some<'tcx>( Applicability::MachineApplicable, ); } else { - span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); } } // lint if `find()` is called by `String` or `&str` @@ -3109,9 +3109,7 @@ fn lint_search_is_some<'tcx>( if is_string_or_str_slice(&search_args[0]); if is_string_or_str_slice(&search_args[1]); then { - let msg = "called `is_some()` after calling `find()` \ - on a string. This is more succinctly expressed by calling \ - `contains()`."; + let msg = "called `is_some()` after calling `find()` on a string"; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( @@ -3119,7 +3117,7 @@ fn lint_search_is_some<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), msg, - "try this", + "use `contains()` instead", format!("contains({})", find_arg), applicability, ); diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index a124ab1dfd4..43827a6a98d 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some.rs:13:13 | LL | let _ = v.iter().find(|&x| { @@ -9,8 +9,9 @@ LL | | ).is_some(); | |______________________________^ | = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with position --> $DIR/search_is_some.rs:19:13 | LL | let _ = v.iter().position(|&x| { @@ -19,8 +20,10 @@ LL | | x < 0 LL | | } LL | | ).is_some(); | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with rposition --> $DIR/search_is_some.rs:25:13 | LL | let _ = v.iter().rposition(|&x| { @@ -29,6 +32,8 @@ LL | | x < 0 LL | | } LL | | ).is_some(); | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` error: use of a blacklisted/placeholder name `foo` --> $DIR/search_is_some.rs:31:9 diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index 7a2c063fee8..f4c5d7a3389 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -1,94 +1,94 @@ -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:10:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:11:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:12:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:13:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with position --> $DIR/search_is_some_fixable.rs:16:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with rposition --> $DIR/search_is_some_fixable.rs:19:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:24:27 | LL | let _ = "hello world".find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:25:27 | LL | let _ = "hello world".find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:26:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:28:16 | LL | let _ = s1.find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:29:16 | LL | let _ = s1.find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:30:16 | LL | let _ = s1.find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:32:21 | LL | let _ = s1[2..].find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:33:21 | LL | let _ = s1[2..].find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:34:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` error: aborting due to 15 previous errors From 56d252c53d1d4f7822b336d086659c26a8f0bb4d Mon Sep 17 00:00:00 2001 From: rsulli55 Date: Tue, 10 Nov 2020 23:17:46 -0700 Subject: [PATCH 0935/1110] Update clippy_lints/src/methods/mod.rs Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 66f5aa0c6a0..5c1c1594f7d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3053,7 +3053,7 @@ fn lint_search_is_some<'tcx>( // lint if caller of search is an Iterator if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { let msg = format!( - "called `is_some()` after searching an `Iterator` with {}", + "called `is_some()` after searching an `Iterator` with `{}`", search_method ); let hint = "this is more succinctly expressed by calling `any()`"; From 5c1c50ee174f20574ddbf67b43ab0e9bb5b2c60d Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Tue, 10 Nov 2020 23:48:01 -0700 Subject: [PATCH 0936/1110] Change variable named `foo` and rerun `update-all-references` --- tests/ui/search_is_some.rs | 8 ++++---- tests/ui/search_is_some.stderr | 16 ++++------------ tests/ui/search_is_some_fixable.stderr | 12 ++++++------ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 1399138a0d2..f0dc3b3d06b 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -28,10 +28,10 @@ fn main() { ).is_some(); // Check that we don't lint if the caller is not an `Iterator` or string - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.find().is_some(); - let _ = foo.position().is_some(); - let _ = foo.rposition().is_some(); + let falsepos = IteratorFalsePositives { foo: 0 }; + let _ = falsepos.find().is_some(); + let _ = falsepos.position().is_some(); + let _ = falsepos.rposition().is_some(); // check that we don't lint if `find()` is called with // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index 43827a6a98d..c601f568c60 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:13:13 | LL | let _ = v.iter().find(|&x| { @@ -11,7 +11,7 @@ LL | | ).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with position +error: called `is_some()` after searching an `Iterator` with `position` --> $DIR/search_is_some.rs:19:13 | LL | let _ = v.iter().position(|&x| { @@ -23,7 +23,7 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with rposition +error: called `is_some()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some.rs:25:13 | LL | let _ = v.iter().rposition(|&x| { @@ -35,13 +35,5 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: use of a blacklisted/placeholder name `foo` - --> $DIR/search_is_some.rs:31:9 - | -LL | let foo = IteratorFalsePositives { foo: 0 }; - | ^^^ - | - = note: `-D clippy::blacklisted-name` implied by `-D warnings` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index f4c5d7a3389..23c1d9a901b 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:10:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); @@ -6,31 +6,31 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | = note: `-D clippy::search-is-some` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:11:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:12:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:13:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` -error: called `is_some()` after searching an `Iterator` with position +error: called `is_some()` after searching an `Iterator` with `position` --> $DIR/search_is_some_fixable.rs:16:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after searching an `Iterator` with rposition +error: called `is_some()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some_fixable.rs:19:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); From 5f64867e1d5344da8ff7526308d4b63676242c0d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 11 Nov 2020 22:36:53 +0900 Subject: [PATCH 0937/1110] Fix suggestion in `manual_range_contains` when using float --- clippy_lints/src/ranges.rs | 6 ++++-- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ tests/ui/range_contains.stderr | 14 +++++++++++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 79e9a56af9a..4b514bbd42c 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -222,13 +222,14 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + let space = if lo.ends_with('.') { " " } else { "" }; span_lint_and_sugg( cx, MANUAL_RANGE_CONTAINS, span, &format!("manual `{}::contains` implementation", range_type), "use", - format!("({}{}{}).contains(&{})", lo, range_op, hi, name), + format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); } else if !combine_and && ord == Some(lord) { @@ -251,13 +252,14 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + let space = if lo.ends_with('.') { " " } else { "" }; span_lint_and_sugg( cx, MANUAL_RANGE_CONTAINS, span, &format!("manual `!{}::contains` implementation", range_type), "use", - format!("!({}{}{}).contains(&{})", lo, range_op, hi, name), + format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); } diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 632a6592a28..048874a7f82 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -38,4 +38,9 @@ fn main() { x >= 8 || x >= 12; x < 12 || 12 < x; x >= 8 || x <= 12; + + // Fix #6315 + let y = 3.; + (0. ..1.).contains(&y); + !(0. ..=1.).contains(&y); } diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 6af0d034ef6..60ad259f404 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -38,4 +38,9 @@ fn main() { x >= 8 || x >= 12; x < 12 || 12 < x; x >= 8 || x <= 12; + + // Fix #6315 + let y = 3.; + y >= 0. && y < 1.; + y < 0. || y > 1.; } diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 69b009eafc3..bc79f1bca84 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -72,5 +72,17 @@ error: manual `!RangeInclusive::contains` implementation LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` -error: aborting due to 12 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:44:5 + | +LL | y >= 0. && y < 1.; + | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:45:5 + | +LL | y < 0. || y > 1.; + | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` + +error: aborting due to 14 previous errors From 8f89108533690afe964799d8f514956ec8b72377 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 9 Nov 2020 22:14:11 +0900 Subject: [PATCH 0938/1110] Fix FP in indirect `needless_collect` when used multiple times --- clippy_lints/src/loops.rs | 34 ++++++++++++++++++++++++++- tests/ui/needless_collect_indirect.rs | 20 ++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0d31e9cfc3d..143cbea5537 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2950,7 +2950,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo for ref stmt in block.stmts { if_chain! { if let StmtKind::Local( - Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } ) = stmt.kind; if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; @@ -2964,6 +2964,16 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if iter_calls.len() == 1; then { + let mut used_count_visitor = UsedCountVisitor { + cx, + id: *pat_id, + count: 0, + }; + walk_block(&mut used_count_visitor, block); + if used_count_visitor.count > 1 { + return; + } + // Suggest replacing iter_call with iter_replacement, and removing stmt let iter_call = &iter_calls[0]; span_lint_and_then( @@ -3087,6 +3097,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { } } +struct UsedCountVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + id: HirId, + count: usize, +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if same_var(self.cx, expr, self.id) { + self.count += 1; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + /// Detect the occurrences of calls to `iter` or `into_iter` for the /// given identifier fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 4f6e5357727..0918a6868ab 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -22,4 +22,24 @@ fn main() { let sample = vec![a.clone(), "b".to_string(), "c".to_string()]; let non_copy_contains = sample.into_iter().collect::>(); non_copy_contains.contains(&a); + + // Fix #5991 + let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let vec_b = vec_a.iter().collect::>(); + if vec_b.len() > 3 {} + let other_vec = vec![1, 3, 12, 4, 16, 2]; + let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::>(); + + // Fix #6297 + let sample = [1; 5]; + let multiple_indirect = sample.iter().collect::>(); + let sample2 = vec![2, 3]; + if multiple_indirect.is_empty() { + // do something + } else { + let found = sample2 + .iter() + .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0)) + .collect::>(); + } } From c6b74df6807792aae1b18031017a3d154c770173 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 13 Nov 2020 14:47:23 +0900 Subject: [PATCH 0939/1110] Fix dogfood test --- clippy_lints/src/utils/ast_utils.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 9050b9b2d9a..fcf7a4b1367 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -110,8 +110,7 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { match (l, r) { (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), - (StructRest::Rest(_), StructRest::Rest(_)) => true, - (StructRest::None, StructRest::None) => true, + (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, _ => false, } } From 0e803417f997ba35c0045704dd347e64c2a1786c Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 16 Nov 2020 12:14:10 +0900 Subject: [PATCH 0940/1110] Add `rustfmt::skip` as a work around because comments are checked and removed by rustfmt for some reason --- tests/ui/cast_ref_to_mut.rs | 4 ++++ tests/ui/cast_ref_to_mut.stderr | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs index 089e5cfabe4..0ede958d170 100644 --- a/tests/ui/cast_ref_to_mut.rs +++ b/tests/ui/cast_ref_to_mut.rs @@ -2,6 +2,10 @@ #![allow(clippy::no_effect)] extern "C" { + #[rustfmt::skip] + // TODO: This `rustfmt::skip` is a work around of #6336 because + // the following comments are checked by rustfmt for some reason. + // // N.B., mutability can be easily incorrect in FFI calls -- as // in C, the default is mutable pointers. fn ffi(c: *mut u8); diff --git a/tests/ui/cast_ref_to_mut.stderr b/tests/ui/cast_ref_to_mut.stderr index aacd99437d9..d36aa0e00ee 100644 --- a/tests/ui/cast_ref_to_mut.stderr +++ b/tests/ui/cast_ref_to_mut.stderr @@ -1,5 +1,5 @@ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:18:9 + --> $DIR/cast_ref_to_mut.rs:22:9 | LL | (*(a as *const _ as *mut String)).push_str(" world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | (*(a as *const _ as *mut String)).push_str(" world"); = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:19:9 + --> $DIR/cast_ref_to_mut.rs:23:9 | LL | *(a as *const _ as *mut _) = String::from("Replaced"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:20:9 + --> $DIR/cast_ref_to_mut.rs:24:9 | LL | *(a as *const _ as *mut String) += " world"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 5b8f2b6c93ea819cd6d6e02ad7e2b8b9da23fd67 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 16 Nov 2020 18:32:01 +0100 Subject: [PATCH 0941/1110] Remove `expect()` calls to avoid ICEs in `deref_addrof` lint --- clippy_lints/src/reference.rs | 48 ++++++++++++++++++++--------------- tests/ui/crashes/ice-6332.rs | 11 ++++++++ 2 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 tests/ui/crashes/ice-6332.rs diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 35a1310d68b..efe3237990d 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -60,30 +60,38 @@ impl EarlyLintPass for DerefAddrOf { }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) }; - let rpos = if *mutability == Mutability::Mut { - macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() - } else { - macro_source.rfind('&').expect("already checked this is a reference") + "&".len() + let mut generate_snippet = |pattern: &str| { + #[allow(clippy::cast_possible_truncation)] + macro_source.rfind(pattern).map(|pattern_pos| { + let rpos = pattern_pos + pattern.len(); + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability) + }) }; - #[allow(clippy::cast_possible_truncation)] - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) + + if *mutability == Mutability::Mut { + generate_snippet("mut") + } else { + generate_snippet("&") + } } else { - snippet_with_applicability(cx, e.span, "_", &mut applicability) + Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) } } else { - snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability) - }.to_string(); - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try this", - sugg, - applicability, - ); + Some(snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)) + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try this", + sugg.to_string(), + applicability, + ); + } } } } diff --git a/tests/ui/crashes/ice-6332.rs b/tests/ui/crashes/ice-6332.rs new file mode 100644 index 00000000000..9dc92aa500b --- /dev/null +++ b/tests/ui/crashes/ice-6332.rs @@ -0,0 +1,11 @@ +fn cmark_check() { + let mut link_err = false; + macro_rules! cmark_error { + ($bad:expr) => { + *$bad = true; + }; + } + cmark_error!(&mut link_err); +} + +pub fn main() {} From a7ac441760ae034ff7401439b38da821f4e2df3a Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 20 Sep 2020 02:03:14 +0900 Subject: [PATCH 0942/1110] Add new lint to detect unnecessarily wrapped value --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unnecessary_wrap.rs | 177 +++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/unnecessary_wrap.fixed | 47 +++++++ tests/ui/unnecessary_wrap.rs | 47 +++++++ tests/ui/unnecessary_wrap.stderr | 34 +++++ 7 files changed, 318 insertions(+) create mode 100644 clippy_lints/src/unnecessary_wrap.rs create mode 100644 tests/ui/unnecessary_wrap.fixed create mode 100644 tests/ui/unnecessary_wrap.rs create mode 100644 tests/ui/unnecessary_wrap.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 816d25bcd93..02b862d3196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2008,6 +2008,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 20b38cbb6d0..2d1f75391bb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,6 +323,7 @@ mod unicode; mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; +mod unnecessary_wrap; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -892,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnecessary_wrap::UNNECESSARY_WRAP, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); + store.register_late_pass(|| box unnecessary_wrap::UnnecessaryWrap); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1571,6 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1775,6 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs new file mode 100644 index 00000000000..26a57517258 --- /dev/null +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -0,0 +1,177 @@ +use crate::utils::{ + is_type_diagnostic_item, match_qpath, multispan_sugg_with_applicability, paths, return_ty, snippet, + span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for private functions that only return `Ok` or `Some`. + /// + /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. + /// + /// **Known problems:** Since this lint changes function type signature, you may need to + /// adjust some codes at callee side. + /// + /// **Example:** + /// + /// ```rust + /// pub fn get_cool_number(a: bool, b: bool) -> Option { + /// if a && b { + /// return Some(50); + /// } + /// if a { + /// Some(0) + /// } else { + /// Some(10) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn get_cool_number(a: bool, b: bool) -> i32 { + /// if a && b { + /// return 50; + /// } + /// if a { + /// 0 + /// } else { + /// 10 + /// } + /// } + /// ``` + pub UNNECESSARY_WRAP, + complexity, + "functions that only return `Ok` or `Some`" +} + +declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if_chain! { + if let FnKind::ItemFn(.., visibility, _) = fn_kind; + if visibility.node.is_pub(); + then { + return; + } + } + + if let ExprKind::Block(ref block, ..) = body.value.kind { + let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + &paths::OPTION_SOME + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + &paths::RESULT_OK + } else { + return; + }; + + let mut visitor = UnnecessaryWrapVisitor { result: Vec::new() }; + visitor.visit_block(block); + let result = visitor.result; + + if result.iter().any(|expr| { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + false + } else { + true + } + } + }) { + return; + } + + let suggs = result.iter().filter_map(|expr| { + let snippet = if let ExprKind::Call(_, ref args) = expr.kind { + Some(snippet(cx, args[0].span, "..").to_string()) + } else { + None + }; + snippet.map(|snip| (expr.span, snip)) + }); + + span_lint_and_then( + cx, + UNNECESSARY_WRAP, + span, + "this function returns unnecessarily wrapping data", + move |diag| { + multispan_sugg_with_applicability( + diag, + "factor this out to", + Applicability::MachineApplicable, + suggs, + ); + }, + ); + } + } +} + +struct UnnecessaryWrapVisitor<'tcx> { + result: Vec<&'tcx Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for UnnecessaryWrapVisitor<'tcx> { + type Map = Map<'tcx>; + + fn visit_block(&mut self, block: &'tcx Block<'tcx>) { + for stmt in block.stmts { + self.visit_stmt(stmt); + } + if let Some(expr) = block.expr { + self.visit_expr(expr) + } + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) { + match stmt.kind { + StmtKind::Semi(ref expr) => { + if let ExprKind::Ret(Some(value)) = expr.kind { + self.result.push(value); + } + }, + StmtKind::Expr(ref expr) => self.visit_expr(expr), + _ => (), + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + ExprKind::Ret(Some(value)) => self.result.push(value), + ExprKind::Call(..) | ExprKind::Path(..) => self.result.push(expr), + ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => { + self.visit_block(block); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.visit_expr(arm.body); + } + }, + _ => intravisit::walk_expr(self, expr), + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 69acd3d9b8b..4a0cdc5d82f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2608,6 +2608,13 @@ vec![ deprecation: None, module: "unwrap", }, + Lint { + name: "unnecessary_wrap", + group: "complexity", + desc: "functions that only return `Ok` or `Some`", + deprecation: None, + module: "unnecessary_wrap", + }, Lint { name: "unneeded_field_pattern", group: "restriction", diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed new file mode 100644 index 00000000000..1657a3173db --- /dev/null +++ b/tests/ui/unnecessary_wrap.fixed @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::unnecessary_wrap)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// public fns should not be linted +pub fn func2(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func4() -> Option { + 1 +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true); +} diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs new file mode 100644 index 00000000000..edf41dad790 --- /dev/null +++ b/tests/ui/unnecessary_wrap.rs @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::unnecessary_wrap)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// public fns should not be linted +pub fn func2(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func4() -> Option { + Some(1) +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true); +} diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr new file mode 100644 index 00000000000..8473bd81839 --- /dev/null +++ b/tests/ui/unnecessary_wrap.stderr @@ -0,0 +1,34 @@ +error: this function unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:8:1 + | +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` +help: factor this out to + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:39:1 + | +LL | / fn func4() -> Option { +LL | | Some(1) + | | ------- help: factor this out to: `1` +LL | | } + | |_^ + +error: aborting due to 2 previous errors + From 750c118b347af938383c5bff53040480e0974071 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 20 Sep 2020 18:22:01 +0900 Subject: [PATCH 0943/1110] Add suggestion on type signatures --- clippy_lints/src/unnecessary_wrap.rs | 31 +++++++++++++++++------- tests/ui/unnecessary_wrap.fixed | 18 +++++++++++++- tests/ui/unnecessary_wrap.rs | 16 +++++++++++++ tests/ui/unnecessary_wrap.stderr | 35 +++++++++++++++++++++------- 4 files changed, 82 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 26a57517258..7b586c1df0c 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; +use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -100,14 +100,27 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { return; } - let suggs = result.iter().filter_map(|expr| { - let snippet = if let ExprKind::Call(_, ref args) = expr.kind { - Some(snippet(cx, args[0].span, "..").to_string()) - } else { - None - }; - snippet.map(|snip| (expr.span, snip)) - }); + let suggs = result + .iter() + .filter_map(|expr| { + let snippet = if let ExprKind::Call(_, ref args) = expr.kind { + Some(snippet(cx, args[0].span, "..").to_string()) + } else { + None + }; + snippet.map(|snip| (expr.span, snip)) + }) + .chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // first outermost inner type is needed + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }); span_lint_and_then( cx, diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed index 1657a3173db..749bb95c417 100644 --- a/tests/ui/unnecessary_wrap.fixed +++ b/tests/ui/unnecessary_wrap.fixed @@ -1,8 +1,10 @@ // run-rustfix + #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] +#![allow(dead_code)] // should be linted fn func1(a: bool, b: bool) -> Option { @@ -37,7 +39,21 @@ fn func3(a: bool) -> Option { // should be linted fn func4() -> Option { - 1 + Some(1) +} + +// should be linted +fn func5() -> Result { + Ok(1) +} + +// should not be linted +fn func6(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } } fn main() { diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index edf41dad790..749bb95c417 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -1,8 +1,10 @@ // run-rustfix + #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] +#![allow(dead_code)] // should be linted fn func1(a: bool, b: bool) -> Option { @@ -40,6 +42,20 @@ fn func4() -> Option { Some(1) } +// should be linted +fn func5() -> Result { + Ok(1) +} + +// should not be linted +fn func6(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 8473bd81839..511d085c82f 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,5 +1,5 @@ -error: this function unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:8:1 +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:10:1 | LL | / fn func1(a: bool, b: bool) -> Option { LL | | if a && b { @@ -13,22 +13,41 @@ LL | | } = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` help: factor this out to | +LL | fn func1(a: bool, b: bool) -> i32 { +LL | if a && b { LL | return 42; LL | } LL | if a { LL | Some(-1); -LL | 2 -LL | } else { ... -error: this function unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:39:1 +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:41:1 | LL | / fn func4() -> Option { LL | | Some(1) - | | ------- help: factor this out to: `1` LL | | } | |_^ + | +help: factor this out to + | +LL | fn func4() -> i32 { +LL | 1 + | -error: aborting due to 2 previous errors +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:46:1 + | +LL | / fn func5() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: factor this out to + | +LL | fn func5() -> i32 { +LL | 1 + | + +error: aborting due to 3 previous errors From 0335b8d6a747672c0a4ff6cb14b12eced3848325 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 21 Sep 2020 00:06:25 +0900 Subject: [PATCH 0944/1110] Fix lint example --- clippy_lints/src/unnecessary_wrap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 7b586c1df0c..c5d0d510079 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// pub fn get_cool_number(a: bool, b: bool) -> Option { + /// fn get_cool_number(a: bool, b: bool) -> Option { /// if a && b { /// return Some(50); /// } @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// pub fn get_cool_number(a: bool, b: bool) -> i32 { + /// fn get_cool_number(a: bool, b: bool) -> i32 { /// if a && b { /// return 50; /// } From 0e9d227c043c1b990912508662e2e5158383ea54 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 21 Sep 2020 00:11:28 +0900 Subject: [PATCH 0945/1110] Add test cases --- tests/ui/unnecessary_wrap.fixed | 14 ++++++++++++-- tests/ui/unnecessary_wrap.rs | 14 ++++++++++++-- tests/ui/unnecessary_wrap.stderr | 6 +++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed index 749bb95c417..e7e3120f82f 100644 --- a/tests/ui/unnecessary_wrap.fixed +++ b/tests/ui/unnecessary_wrap.fixed @@ -42,13 +42,18 @@ fn func4() -> Option { Some(1) } +// should not be linted +fn func5() -> Option { + None +} + // should be linted -fn func5() -> Result { +fn func6() -> Result { Ok(1) } // should not be linted -fn func6(a: bool) -> Result { +fn func7(a: bool) -> Result { if a { Ok(1) } else { @@ -56,6 +61,11 @@ fn func6(a: bool) -> Result { } } +// should not be linted +fn func8(a: bool) -> Result { + Err(()) +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 749bb95c417..e7e3120f82f 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -42,13 +42,18 @@ fn func4() -> Option { Some(1) } +// should not be linted +fn func5() -> Option { + None +} + // should be linted -fn func5() -> Result { +fn func6() -> Result { Ok(1) } // should not be linted -fn func6(a: bool) -> Result { +fn func7(a: bool) -> Result { if a { Ok(1) } else { @@ -56,6 +61,11 @@ fn func6(a: bool) -> Result { } } +// should not be linted +fn func8(a: bool) -> Result { + Err(()) +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 511d085c82f..76859fac589 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -36,16 +36,16 @@ LL | 1 | error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:46:1 + --> $DIR/unnecessary_wrap.rs:51:1 | -LL | / fn func5() -> Result { +LL | / fn func6() -> Result { LL | | Ok(1) LL | | } | |_^ | help: factor this out to | -LL | fn func5() -> i32 { +LL | fn func6() -> i32 { LL | 1 | From ebdd4e2c723c6902851a050ec8bdc7b966dc2c64 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 02:53:55 +0900 Subject: [PATCH 0946/1110] Refactor code according to reivews --- clippy_lints/src/unnecessary_wrap.rs | 236 +++++++++++++++++---------- 1 file changed, 147 insertions(+), 89 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index c5d0d510079..53ade7baec7 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -4,7 +4,7 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; @@ -71,57 +71,31 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } - if let ExprKind::Block(ref block, ..) = body.value.kind { - let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - &paths::OPTION_SOME - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - &paths::RESULT_OK - } else { - return; - }; + let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + &paths::OPTION_SOME + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + &paths::RESULT_OK + } else { + return; + }; - let mut visitor = UnnecessaryWrapVisitor { result: Vec::new() }; - visitor.visit_block(block); - let result = visitor.result; - - if result.iter().any(|expr| { - if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, path); - if args.len() == 1; - then { - false - } else { - true - } + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { + if_chain! { + if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); + true + } else { + false } - }) { - return; } + }); - let suggs = result - .iter() - .filter_map(|expr| { - let snippet = if let ExprKind::Call(_, ref args) = expr.kind { - Some(snippet(cx, args[0].span, "..").to_string()) - } else { - None - }; - snippet.map(|snip| (expr.span, snip)) - }) - .chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // first outermost inner type is needed - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }); - + if can_sugg { span_lint_and_then( cx, UNNECESSARY_WRAP, @@ -132,7 +106,17 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { diag, "factor this out to", Applicability::MachineApplicable, - suggs, + suggs.into_iter().chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }), ); }, ); @@ -140,51 +124,125 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } -struct UnnecessaryWrapVisitor<'tcx> { - result: Vec<&'tcx Expr<'tcx>>, +// code below is copied from `bind_instead_of_map` + +fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + ExprKind::Block(..) => intravisit::walk_expr(self, expr), + ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } } -impl<'tcx> Visitor<'tcx> for UnnecessaryWrapVisitor<'tcx> { - type Map = Map<'tcx>; +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } - fn visit_block(&mut self, block: &'tcx Block<'tcx>) { - for stmt in block.stmts { - self.visit_stmt(stmt); + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None } - if let Some(expr) = block.expr { - self.visit_expr(expr) + + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } } } - fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) { - match stmt.kind { - StmtKind::Semi(ref expr) => { - if let ExprKind::Ret(Some(value)) = expr.kind { - self.result.push(value); - } - }, - StmtKind::Expr(ref expr) => self.visit_expr(expr), - _ => (), - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - ExprKind::Ret(Some(value)) => self.result.push(value), - ExprKind::Call(..) | ExprKind::Path(..) => self.result.push(expr), - ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => { - self.visit_block(block); - }, - ExprKind::Match(_, arms, _) => { - for arm in arms { - self.visit_expr(arm.body); - } - }, - _ => intravisit::walk_expr(self, expr), - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found } From 6a62390c86cb9f72465f1c314b64c94c273956b7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 02:57:47 +0900 Subject: [PATCH 0947/1110] Optout rustfix test --- tests/ui/unnecessary_wrap.fixed | 73 -------------------------------- tests/ui/unnecessary_wrap.rs | 2 - tests/ui/unnecessary_wrap.stderr | 6 +-- 3 files changed, 3 insertions(+), 78 deletions(-) delete mode 100644 tests/ui/unnecessary_wrap.fixed diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed deleted file mode 100644 index e7e3120f82f..00000000000 --- a/tests/ui/unnecessary_wrap.fixed +++ /dev/null @@ -1,73 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_wrap)] -#![allow(clippy::no_effect)] -#![allow(clippy::needless_return)] -#![allow(clippy::if_same_then_else)] -#![allow(dead_code)] - -// should be linted -fn func1(a: bool, b: bool) -> Option { - if a && b { - return Some(42); - } - if a { - Some(-1); - Some(2) - } else { - return Some(1337); - } -} - -// public fns should not be linted -pub fn func2(a: bool) -> Option { - if a { - Some(1) - } else { - Some(1) - } -} - -// should not be linted -fn func3(a: bool) -> Option { - if a { - Some(1) - } else { - None - } -} - -// should be linted -fn func4() -> Option { - Some(1) -} - -// should not be linted -fn func5() -> Option { - None -} - -// should be linted -fn func6() -> Result { - Ok(1) -} - -// should not be linted -fn func7(a: bool) -> Result { - if a { - Ok(1) - } else { - Err(()) - } -} - -// should not be linted -fn func8(a: bool) -> Result { - Err(()) -} - -fn main() { - // method calls are not linted - func1(true, true); - func2(true); -} diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index e7e3120f82f..f78a7604a5a 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -1,5 +1,3 @@ -// run-rustfix - #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 76859fac589..cd05104f490 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,5 +1,5 @@ error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:10:1 + --> $DIR/unnecessary_wrap.rs:8:1 | LL | / fn func1(a: bool, b: bool) -> Option { LL | | if a && b { @@ -22,7 +22,7 @@ LL | Some(-1); ... error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:41:1 + --> $DIR/unnecessary_wrap.rs:39:1 | LL | / fn func4() -> Option { LL | | Some(1) @@ -36,7 +36,7 @@ LL | 1 | error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:51:1 + --> $DIR/unnecessary_wrap.rs:49:1 | LL | / fn func6() -> Result { LL | | Ok(1) From 3ed89026231a17beed9e93b055ea1b6192997a68 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 03:06:52 +0900 Subject: [PATCH 0948/1110] Fix typo Co-authored-by: Philipp Krones --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 53ade7baec7..e5ef7cde789 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. /// /// **Known problems:** Since this lint changes function type signature, you may need to - /// adjust some codes at callee side. + /// adjust some code at callee side. /// /// **Example:** /// From c77585672720285db5180b2ee4207c7ee9b51072 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 03:42:58 +0900 Subject: [PATCH 0949/1110] Call `diag.multipart_suggestion` instead --- clippy_lints/src/unnecessary_wrap.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index e5ef7cde789..d581912284f 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_qpath, multispan_sugg_with_applicability, paths, return_ty, snippet, + is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, }; use if_chain::if_chain; @@ -102,10 +102,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span, "this function returns unnecessarily wrapping data", move |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "factor this out to", - Applicability::MachineApplicable, suggs.into_iter().chain({ let inner_ty = return_ty(cx, hir_id) .walk() @@ -116,7 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { _ => None, }); inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }), + }).collect(), + Applicability::MachineApplicable, ); }, ); From a433d4690e04c8c8eceb1f59c78fded1f04cf6f0 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 03:48:15 +0900 Subject: [PATCH 0950/1110] Run rustfmt --- clippy_lints/src/unnecessary_wrap.rs | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index d581912284f..1c8a08172a3 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,7 +1,4 @@ -use crate::utils::{ - is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, - span_lint_and_then, -}; +use crate::utils::{is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor}; @@ -104,17 +101,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { move |diag| { diag.multipart_suggestion( "factor this out to", - suggs.into_iter().chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }).collect(), + suggs + .into_iter() + .chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }) + .collect(), Applicability::MachineApplicable, ); }, From cdb72df6f9f9d6946b0614b01e70bc9c46edfe89 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 26 Sep 2020 15:39:39 +0900 Subject: [PATCH 0951/1110] Split lint suggestion into two --- clippy_lints/src/unnecessary_wrap.rs | 41 +++++++++++++++------------- tests/ui/unnecessary_wrap.stderr | 18 +++++++++--- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 1c8a08172a3..3ddf921a04b 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -68,10 +68,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } - let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - &paths::OPTION_SOME + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + ("Option", &paths::OPTION_SOME) } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - &paths::RESULT_OK + ("Result", &paths::RESULT_OK) } else { return; }; @@ -98,23 +98,26 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { UNNECESSARY_WRAP, span, "this function returns unnecessarily wrapping data", - move |diag| { + |diag| { + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.for_each(|inner_ty| { + diag.span_suggestion( + fn_decl.output.span(), + format!("remove `{}` from the return type...", return_type).as_str(), + inner_ty, + Applicability::MachineApplicable, + ); + }); diag.multipart_suggestion( - "factor this out to", - suggs - .into_iter() - .chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }) - .collect(), + "...and change the returning expressions", + suggs, Applicability::MachineApplicable, ); }, diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index cd05104f490..7833ee4b213 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -11,14 +11,18 @@ LL | | } | |_^ | = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` -help: factor this out to +help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { -LL | if a && b { + | ^^^ +help: ...and change the returning expressions + | LL | return 42; LL | } LL | if a { LL | Some(-1); +LL | 2 +LL | } else { ... error: this function returns unnecessarily wrapping data @@ -29,9 +33,12 @@ LL | | Some(1) LL | | } | |_^ | -help: factor this out to +help: remove `Option` from the return type... | LL | fn func4() -> i32 { + | ^^^ +help: ...and change the returning expressions + | LL | 1 | @@ -43,9 +50,12 @@ LL | | Ok(1) LL | | } | |_^ | -help: factor this out to +help: remove `Result` from the return type... | LL | fn func6() -> i32 { + | ^^^ +help: ...and change the returning expressions + | LL | 1 | From 6b55f3fec98fa8fd4ca15edbf1cc1ab86d22f08f Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 26 Sep 2020 16:27:10 +0900 Subject: [PATCH 0952/1110] Add test case --- tests/ui/unnecessary_wrap.rs | 28 +++++++++++++++------- tests/ui/unnecessary_wrap.stderr | 40 ++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index f78a7604a5a..6037f5807d3 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -17,8 +17,20 @@ fn func1(a: bool, b: bool) -> Option { } } +// should be linted +fn func2(a: bool, b: bool) -> Option { + if a && b { + return Some(10); + } + if a { + Some(20) + } else { + Some(30) + } +} + // public fns should not be linted -pub fn func2(a: bool) -> Option { +pub fn func3(a: bool) -> Option { if a { Some(1) } else { @@ -27,7 +39,7 @@ pub fn func2(a: bool) -> Option { } // should not be linted -fn func3(a: bool) -> Option { +fn func4(a: bool) -> Option { if a { Some(1) } else { @@ -36,22 +48,22 @@ fn func3(a: bool) -> Option { } // should be linted -fn func4() -> Option { +fn func5() -> Option { Some(1) } // should not be linted -fn func5() -> Option { +fn func6() -> Option { None } // should be linted -fn func6() -> Result { +fn func7() -> Result { Ok(1) } // should not be linted -fn func7(a: bool) -> Result { +fn func8(a: bool) -> Result { if a { Ok(1) } else { @@ -60,12 +72,12 @@ fn func7(a: bool) -> Result { } // should not be linted -fn func8(a: bool) -> Result { +fn func9(a: bool) -> Result { Err(()) } fn main() { // method calls are not linted func1(true, true); - func2(true); + func2(true, true); } diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 7833ee4b213..a3481330e99 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -26,16 +26,42 @@ LL | } else { ... error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:39:1 + --> $DIR/unnecessary_wrap.rs:21:1 | -LL | / fn func4() -> Option { +LL | / fn func2(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(10); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func2(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 10; +LL | } +LL | if a { +LL | 20 +LL | } else { +LL | 30 + | + +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:51:1 + | +LL | / fn func5() -> Option { LL | | Some(1) LL | | } | |_^ | help: remove `Option` from the return type... | -LL | fn func4() -> i32 { +LL | fn func5() -> i32 { | ^^^ help: ...and change the returning expressions | @@ -43,21 +69,21 @@ LL | 1 | error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:49:1 + --> $DIR/unnecessary_wrap.rs:61:1 | -LL | / fn func6() -> Result { +LL | / fn func7() -> Result { LL | | Ok(1) LL | | } | |_^ | help: remove `Result` from the return type... | -LL | fn func6() -> i32 { +LL | fn func7() -> i32 { | ^^^ help: ...and change the returning expressions | LL | 1 | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From df0d565e597ad9c436fadfb8e11a16a92bcb4114 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 2 Oct 2020 18:21:17 +0900 Subject: [PATCH 0953/1110] Move `find_all_ret_expressions` into `utils` --- .../src/methods/bind_instead_of_map.rs | 125 +---------------- clippy_lints/src/unnecessary_wrap.rs | 132 +----------------- clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/visitors.rs | 125 +++++++++++++++++ 4 files changed, 133 insertions(+), 250 deletions(-) create mode 100644 clippy_lints/src/utils/visitors.rs diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index ae37942e55a..540a1484a85 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,14 +1,12 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use crate::utils::{ in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, + snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; use rustc_span::Span; pub(crate) struct OptionAndThenSome; @@ -193,124 +191,3 @@ pub(crate) trait BindInsteadOfMap { } } } - -/// returns `true` if expr contains match expr desugared from try -fn contains_try(expr: &hir::Expr<'_>) -> bool { - struct TryFinder { - found: bool, - } - - impl<'hir> intravisit::Visitor<'hir> for TryFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - if self.found { - return; - } - match expr.kind { - hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, - _ => intravisit::walk_expr(self, expr), - } - } - } - - let mut visitor = TryFinder { found: false }; - visitor.visit_expr(expr); - visitor.found -} - -fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool -where - F: FnMut(&'hir hir::Expr<'hir>) -> bool, -{ - struct RetFinder { - in_stmt: bool, - failed: bool, - cb: F, - } - - struct WithStmtGuarg<'a, F> { - val: &'a mut RetFinder, - prev_in_stmt: bool, - } - - impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { - let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { - val: self, - prev_in_stmt, - } - } - } - - impl std::ops::Deref for WithStmtGuarg<'_, F> { - type Target = RetFinder; - - fn deref(&self) -> &Self::Target { - self.val - } - } - - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.val - } - } - - impl Drop for WithStmtGuarg<'_, F> { - fn drop(&mut self) { - self.val.in_stmt = self.prev_in_stmt; - } - } - - impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { - intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { - if self.failed { - return; - } - if self.in_stmt { - match expr.kind { - hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), - _ => intravisit::walk_expr(self, expr), - } - } else { - match expr.kind { - hir::ExprKind::Match(cond, arms, _) => { - self.inside_stmt(true).visit_expr(cond); - for arm in arms { - self.visit_expr(arm.body); - } - }, - hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), - hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), - _ => self.failed |= !(self.cb)(expr), - } - } - } - } - - !contains_try(expr) && { - let mut ret_finder = RetFinder { - in_stmt: false, - failed: false, - cb: callback, - }; - ret_finder.visit_expr(expr); - !ret_finder.failed - } -} diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 3ddf921a04b..de13301381e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,10 +1,13 @@ -use crate::utils::{is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then}; +use crate::utils::{ + is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -125,126 +128,3 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } } - -// code below is copied from `bind_instead_of_map` - -fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool -where - F: FnMut(&'hir Expr<'hir>) -> bool, -{ - struct RetFinder { - in_stmt: bool, - failed: bool, - cb: F, - } - - struct WithStmtGuarg<'a, F> { - val: &'a mut RetFinder, - prev_in_stmt: bool, - } - - impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { - let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { - val: self, - prev_in_stmt, - } - } - } - - impl std::ops::Deref for WithStmtGuarg<'_, F> { - type Target = RetFinder; - - fn deref(&self) -> &Self::Target { - self.val - } - } - - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.val - } - } - - impl Drop for WithStmtGuarg<'_, F> { - fn drop(&mut self) { - self.val.in_stmt = self.prev_in_stmt; - } - } - - impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) { - intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) - } - - fn visit_expr(&mut self, expr: &'hir Expr<'_>) { - if self.failed { - return; - } - if self.in_stmt { - match expr.kind { - ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), - _ => intravisit::walk_expr(self, expr), - } - } else { - match expr.kind { - ExprKind::Match(cond, arms, _) => { - self.inside_stmt(true).visit_expr(cond); - for arm in arms { - self.visit_expr(arm.body); - } - }, - ExprKind::Block(..) => intravisit::walk_expr(self, expr), - ExprKind::Ret(Some(expr)) => self.visit_expr(expr), - _ => self.failed |= !(self.cb)(expr), - } - } - } - } - - !contains_try(expr) && { - let mut ret_finder = RetFinder { - in_stmt: false, - failed: false, - cb: callback, - }; - ret_finder.visit_expr(expr); - !ret_finder.failed - } -} - -/// returns `true` if expr contains match expr desugared from try -fn contains_try(expr: &Expr<'_>) -> bool { - struct TryFinder { - found: bool, - } - - impl<'hir> intravisit::Visitor<'hir> for TryFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { - if self.found { - return; - } - match expr.kind { - ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true, - _ => intravisit::walk_expr(self, expr), - } - } - } - - let mut visitor = TryFinder { found: false }; - visitor.visit_expr(expr); - visitor.found -} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 253b7823bd9..bd62e67f80e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -21,6 +21,7 @@ pub mod ptr; pub mod qualify_min_const_fn; pub mod sugg; pub mod usage; +pub mod visitors; pub use self::attrs::*; pub use self::diagnostics::*; diff --git a/clippy_lints/src/utils/visitors.rs b/clippy_lints/src/utils/visitors.rs new file mode 100644 index 00000000000..b0837b6c43e --- /dev/null +++ b/clippy_lints/src/utils/visitors.rs @@ -0,0 +1,125 @@ +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &hir::Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } + } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir hir::Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + hir::ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), + hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} From eec7f5c1113d3d49dd7f42462ea25d0b36b4b49b Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:21:04 +0900 Subject: [PATCH 0954/1110] Update clippy_lints/src/unnecessary_wrap.rs Co-authored-by: Philipp Krones --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index de13301381e..28762b8d265 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - "this function returns unnecessarily wrapping data", + format!("this function's return value is unnecessarily wrapped by `{}`, return_type)", |diag| { let inner_ty = return_ty(cx, hir_id) .walk() From 1bdac8712868e1418dcb13e817254620a8c01158 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:27:47 +0900 Subject: [PATCH 0955/1110] Improve lint message --- clippy_lints/src/unnecessary_wrap.rs | 2 +- tests/ui/unnecessary_wrap.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 28762b8d265..db51ee681ef 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - format!("this function's return value is unnecessarily wrapped by `{}`, return_type)", + format!("this function's return value is unnecessarily wrapped by `{}`", return_type).as_str(), |diag| { let inner_ty = return_ty(cx, hir_id) .walk() diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index a3481330e99..958bc998022 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,4 +1,4 @@ -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:8:1 | LL | / fn func1(a: bool, b: bool) -> Option { @@ -25,7 +25,7 @@ LL | 2 LL | } else { ... -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:21:1 | LL | / fn func2(a: bool, b: bool) -> Option { @@ -51,7 +51,7 @@ LL | } else { LL | 30 | -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:51:1 | LL | / fn func5() -> Option { @@ -68,7 +68,7 @@ help: ...and change the returning expressions LL | 1 | -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Result` --> $DIR/unnecessary_wrap.rs:61:1 | LL | / fn func7() -> Result { From 8392bc7946a212a85bc5a411f5321a1f76d5ccf6 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:37:27 +0900 Subject: [PATCH 0956/1110] Run `cargo dev fmt` --- clippy_lints/src/unnecessary_wrap.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index db51ee681ef..fc1a33fc6cd 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - format!("this function's return value is unnecessarily wrapped by `{}`", return_type).as_str(), + format!( + "this function's return value is unnecessarily wrapped by `{}`", + return_type + ) + .as_str(), |diag| { let inner_ty = return_ty(cx, hir_id) .walk() From 2f85aa736e85544803ff6d3e6b13e44c0729bbdd Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 16:30:46 +0900 Subject: [PATCH 0957/1110] Make lint skip closures --- clippy_lints/src/unnecessary_wrap.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index fc1a33fc6cd..a20d1f82e9e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -63,12 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { - if_chain! { - if let FnKind::ItemFn(.., visibility, _) = fn_kind; - if visibility.node.is_pub(); - then { - return; - } + match fn_kind { + FnKind::ItemFn(.., visibility, _) if visibility.node.is_pub() => return, + FnKind::Closure(..) => return, + _ => (), } let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { From 12474c62ff89ab122c2e6881b00680913fdf1d35 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 16:53:18 +0900 Subject: [PATCH 0958/1110] Add support for methods --- clippy_lints/src/unnecessary_wrap.rs | 6 +++++- tests/ui/unnecessary_wrap.rs | 14 ++++++++++++++ tests/ui/unnecessary_wrap.stderr | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index a20d1f82e9e..365e9c9984e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -64,7 +64,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { hir_id: HirId, ) { match fn_kind { - FnKind::ItemFn(.., visibility, _) if visibility.node.is_pub() => return, + FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { + if visibility.node.is_pub() { + return; + } + }, FnKind::Closure(..) => return, _ => (), } diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 6037f5807d3..0ced20b7a56 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -76,6 +76,20 @@ fn func9(a: bool) -> Result { Err(()) } +struct A; + +impl A { + // should not be linted + pub fn func10() -> Option { + Some(1) + } + + // should be linted + fn func11() -> Option { + Some(1) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 958bc998022..dbc5748c29e 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -85,5 +85,22 @@ help: ...and change the returning expressions LL | 1 | -error: aborting due to 4 previous errors +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wrap.rs:88:5 + | +LL | / fn func11() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func11() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: aborting due to 5 previous errors From c5447eb3c167025fcc1b842fabd7bb43a2eb1e9e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:17:45 +0900 Subject: [PATCH 0959/1110] Make lint skip macros --- clippy_lints/src/unnecessary_wrap.rs | 3 ++- tests/ui/unnecessary_wrap.rs | 9 +++++++-- tests/ui/unnecessary_wrap.stderr | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 365e9c9984e..7ec523293c8 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; @@ -84,6 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { + if !in_macro(ret_expr.span); if let ExprKind::Call(ref func, ref args) = ret_expr.kind; if let ExprKind::Path(ref qpath) = func.kind; if match_qpath(qpath, path); diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 0ced20b7a56..618c452065b 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -76,16 +76,21 @@ fn func9(a: bool) -> Result { Err(()) } +// should not be linted +fn func10() -> Option<()> { + unimplemented!() +} + struct A; impl A { // should not be linted - pub fn func10() -> Option { + pub fn func11() -> Option { Some(1) } // should be linted - fn func11() -> Option { + fn func12() -> Option { Some(1) } } diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index dbc5748c29e..5f21b74bc76 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -86,16 +86,16 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:88:5 + --> $DIR/unnecessary_wrap.rs:93:5 | -LL | / fn func11() -> Option { +LL | / fn func12() -> Option { LL | | Some(1) LL | | } | |_____^ | help: remove `Option` from the return type... | -LL | fn func11() -> i32 { +LL | fn func12() -> i32 { | ^^^ help: ...and change the returning expressions | From c7692cf7493facefd520a98bf86aa1f4a031f435 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:27:08 +0900 Subject: [PATCH 0960/1110] Skip function with no exprs contained --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 7ec523293c8..28cc20f0007 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } }); - if can_sugg { + if can_sugg && !suggs.is_empty() { span_lint_and_then( cx, UNNECESSARY_WRAP, From 30632fb8e6e4d6b998f2af7da93931ddb9f5de03 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:55:25 +0900 Subject: [PATCH 0961/1110] Allow this lint on lint tests --- tests/ui/derive_ord_xor_partial_ord.rs | 1 + tests/ui/derive_ord_xor_partial_ord.stderr | 16 ++++----- tests/ui/doc_errors.rs | 1 + tests/ui/drop_ref.rs | 1 + tests/ui/forget_ref.rs | 1 + tests/ui/forget_ref.stderr | 36 +++++++++---------- tests/ui/let_underscore_must_use.rs | 1 + tests/ui/let_underscore_must_use.stderr | 24 ++++++------- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/needless_lifetimes.rs | 2 +- tests/ui/option_map_unit_fn_fixable.fixed | 1 + tests/ui/option_map_unit_fn_fixable.rs | 1 + tests/ui/option_map_unit_fn_fixable.stderr | 36 +++++++++---------- tests/ui/option_option.rs | 1 + tests/ui/option_option.stderr | 20 +++++------ tests/ui/or_fun_call.fixed | 1 + tests/ui/or_fun_call.rs | 1 + tests/ui/or_fun_call.stderr | 20 +++++------ tests/ui/panic_in_result_fn.rs | 1 + tests/ui/panic_in_result_fn.stderr | 24 ++++++------- tests/ui/question_mark.fixed | 1 + tests/ui/question_mark.rs | 1 + tests/ui/question_mark.stderr | 22 ++++++------ tests/ui/redundant_pattern_matching.fixed | 1 + tests/ui/redundant_pattern_matching.rs | 1 + tests/ui/redundant_pattern_matching.stderr | 6 ++-- tests/ui/try_err.fixed | 1 + tests/ui/try_err.rs | 1 + tests/ui/try_err.stderr | 8 ++--- tests/ui/unit_arg.rs | 1 + tests/ui/unit_arg.stderr | 20 +++++------ tests/ui/unnecessary_clone.rs | 2 +- tests/ui/useless_conversion.fixed | 1 + tests/ui/useless_conversion.rs | 1 + tests/ui/useless_conversion.stderr | 22 ++++++------ tests/ui/wildcard_imports.fixed | 1 + tests/ui/wildcard_imports.rs | 1 + tests/ui/wildcard_imports.stderr | 40 +++++++++++----------- 39 files changed, 173 insertions(+), 149 deletions(-) diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index b82dc518a3b..78ec1727fc1 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,4 +1,5 @@ #![warn(clippy::derive_ord_xor_partial_ord)] +#![allow(clippy::unnecessary_wrap)] use std::cmp::Ordering; diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr index 66bc4d42ce8..97b46a4aa89 100644 --- a/tests/ui/derive_ord_xor_partial_ord.stderr +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -1,12 +1,12 @@ error: you are deriving `Ord` but have implemented `PartialOrd` explicitly - --> $DIR/derive_ord_xor_partial_ord.rs:20:10 + --> $DIR/derive_ord_xor_partial_ord.rs:21:10 | LL | #[derive(Ord, PartialEq, Eq)] | ^^^ | = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings` note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:23:1 + --> $DIR/derive_ord_xor_partial_ord.rs:24:1 | LL | / impl PartialOrd for DeriveOrd { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -17,13 +17,13 @@ LL | | } = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Ord` but have implemented `PartialOrd` explicitly - --> $DIR/derive_ord_xor_partial_ord.rs:29:10 + --> $DIR/derive_ord_xor_partial_ord.rs:30:10 | LL | #[derive(Ord, PartialEq, Eq)] | ^^^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:32:1 + --> $DIR/derive_ord_xor_partial_ord.rs:33:1 | LL | / impl PartialOrd for DeriveOrdWithExplicitTypeVariable { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -34,7 +34,7 @@ LL | | } = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` - --> $DIR/derive_ord_xor_partial_ord.rs:41:1 + --> $DIR/derive_ord_xor_partial_ord.rs:42:1 | LL | / impl std::cmp::Ord for DerivePartialOrd { LL | | fn cmp(&self, other: &Self) -> Ordering { @@ -44,14 +44,14 @@ LL | | } | |_^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:38:10 + --> $DIR/derive_ord_xor_partial_ord.rs:39:10 | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` - --> $DIR/derive_ord_xor_partial_ord.rs:61:5 + --> $DIR/derive_ord_xor_partial_ord.rs:62:5 | LL | / impl Ord for DerivePartialOrdInUseOrd { LL | | fn cmp(&self, other: &Self) -> Ordering { @@ -61,7 +61,7 @@ LL | | } | |_____^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:58:14 + --> $DIR/derive_ord_xor_partial_ord.rs:59:14 | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index f47b81a450e..77df7f176f0 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,6 +1,7 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] #![allow(clippy::result_unit_err)] +#![allow(clippy::unnecessary_wrap)] use std::io; diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 6b5bcdaa78e..ba12e763821 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,6 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] +#![allow(clippy::unnecessary_wrap)] use std::mem::drop; diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index 447fdbe1fac..a9c2a92ce6b 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,5 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] +#![allow(clippy::unnecessary_wrap)] use std::mem::forget; diff --git a/tests/ui/forget_ref.stderr b/tests/ui/forget_ref.stderr index f90bcc2762c..b2c7f2023bf 100644 --- a/tests/ui/forget_ref.stderr +++ b/tests/ui/forget_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:9:5 + --> $DIR/forget_ref.rs:10:5 | LL | forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::forget-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:9:12 + --> $DIR/forget_ref.rs:10:12 | LL | forget(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:12:5 + --> $DIR/forget_ref.rs:13:5 | LL | forget(&owned); | ^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:12:12 + --> $DIR/forget_ref.rs:13:12 | LL | forget(&owned); | ^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:13:5 + --> $DIR/forget_ref.rs:14:5 | LL | forget(&&owned); | ^^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/forget_ref.rs:13:12 + --> $DIR/forget_ref.rs:14:12 | LL | forget(&&owned); | ^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:14:5 + --> $DIR/forget_ref.rs:15:5 | LL | forget(&mut owned); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:14:12 + --> $DIR/forget_ref.rs:15:12 | LL | forget(&mut owned); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:18:5 + --> $DIR/forget_ref.rs:19:5 | LL | forget(&*reference1); | ^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:18:12 + --> $DIR/forget_ref.rs:19:12 | LL | forget(&*reference1); | ^^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:21:5 + --> $DIR/forget_ref.rs:22:5 | LL | forget(reference2); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:21:12 + --> $DIR/forget_ref.rs:22:12 | LL | forget(reference2); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:24:5 + --> $DIR/forget_ref.rs:25:5 | LL | forget(reference3); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:24:12 + --> $DIR/forget_ref.rs:25:12 | LL | forget(reference3); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:29:5 + --> $DIR/forget_ref.rs:30:5 | LL | forget(&val); | ^^^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/forget_ref.rs:29:12 + --> $DIR/forget_ref.rs:30:12 | LL | forget(&val); | ^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:37:5 + --> $DIR/forget_ref.rs:38:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:37:22 + --> $DIR/forget_ref.rs:38:22 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index 27dda606067..e3800cda1b1 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -1,4 +1,5 @@ #![warn(clippy::let_underscore_must_use)] +#![allow(clippy::unnecessary_wrap)] // Debug implementations can fire this lint, // so we shouldn't lint external macros diff --git a/tests/ui/let_underscore_must_use.stderr b/tests/ui/let_underscore_must_use.stderr index 447f2419e3b..5b751ea56de 100644 --- a/tests/ui/let_underscore_must_use.stderr +++ b/tests/ui/let_underscore_must_use.stderr @@ -1,5 +1,5 @@ error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:66:5 + --> $DIR/let_underscore_must_use.rs:67:5 | LL | let _ = f(); | ^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = f(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:67:5 + --> $DIR/let_underscore_must_use.rs:68:5 | LL | let _ = g(); | ^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = g(); = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:69:5 + --> $DIR/let_underscore_must_use.rs:70:5 | LL | let _ = l(0_u32); | ^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = l(0_u32); = help: consider explicitly using function result error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:73:5 + --> $DIR/let_underscore_must_use.rs:74:5 | LL | let _ = s.f(); | ^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = s.f(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:74:5 + --> $DIR/let_underscore_must_use.rs:75:5 | LL | let _ = s.g(); | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = s.g(); = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:77:5 + --> $DIR/let_underscore_must_use.rs:78:5 | LL | let _ = S::h(); | ^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = S::h(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:78:5 + --> $DIR/let_underscore_must_use.rs:79:5 | LL | let _ = S::p(); | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = S::p(); = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:80:5 + --> $DIR/let_underscore_must_use.rs:81:5 | LL | let _ = S::a(); | ^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = S::a(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:82:5 + --> $DIR/let_underscore_must_use.rs:83:5 | LL | let _ = if true { Ok(()) } else { Err(()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = if true { Ok(()) } else { Err(()) }; = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:86:5 + --> $DIR/let_underscore_must_use.rs:87:5 | LL | let _ = a.is_ok(); | ^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = a.is_ok(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:88:5 + --> $DIR/let_underscore_must_use.rs:89:5 | LL | let _ = a.map(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = a.map(|_| ()); = help: consider explicitly using expression value error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:90:5 + --> $DIR/let_underscore_must_use.rs:91:5 | LL | let _ = a; | ^^^^^^^^^^ diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index a7ab5a12cb7..b4a51837b2f 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -4,6 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] +#![allow(clippy::unnecessary_wrap)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index e364a05f376..e83cc46bda2 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -4,6 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] +#![allow(clippy::unnecessary_wrap)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 6001ef37eb7..e5973bbef8d 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_lifetimes)] -#![allow(dead_code, clippy::needless_pass_by_value)] +#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wrap)] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 96d1c54946c..de2e9155906 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -2,6 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 931ffc18665..f0887c8a4bc 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -2,6 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index d7d45ef9b0b..8abdbcafb6e 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:38:5 + --> $DIR/option_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -9,7 +9,7 @@ LL | x.field.map(do_nothing); = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:40:5 + --> $DIR/option_map_unit_fn_fixable.rs:41:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -17,7 +17,7 @@ LL | x.field.map(do_nothing); | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:42:5 + --> $DIR/option_map_unit_fn_fixable.rs:43:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -25,7 +25,7 @@ LL | x.field.map(diverge); | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:48:5 + --> $DIR/option_map_unit_fn_fixable.rs:49:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -33,7 +33,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:50:5 + --> $DIR/option_map_unit_fn_fixable.rs:51:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -41,7 +41,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:53:5 + --> $DIR/option_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -49,7 +49,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:55:5 + --> $DIR/option_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -57,7 +57,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:57:5 + --> $DIR/option_map_unit_fn_fixable.rs:58:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -65,7 +65,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:59:5 + --> $DIR/option_map_unit_fn_fixable.rs:60:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -73,7 +73,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:62:5 + --> $DIR/option_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -81,7 +81,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:64:5 + --> $DIR/option_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -89,7 +89,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:66:5 + --> $DIR/option_map_unit_fn_fixable.rs:67:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -97,7 +97,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:68:5 + --> $DIR/option_map_unit_fn_fixable.rs:69:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -105,7 +105,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:73:5 + --> $DIR/option_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -113,7 +113,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:75:5 + --> $DIR/option_map_unit_fn_fixable.rs:76:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -121,7 +121,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:77:5 + --> $DIR/option_map_unit_fn_fixable.rs:78:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -129,7 +129,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:80:5 + --> $DIR/option_map_unit_fn_fixable.rs:81:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -137,7 +137,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:82:5 + --> $DIR/option_map_unit_fn_fixable.rs:83:5 | LL | option().map(do_nothing);} | ^^^^^^^^^^^^^^^^^^^^^^^^- diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 24344833641..557d29dff67 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,4 +1,5 @@ #![deny(clippy::option_option)] +#![allow(clippy::unnecessary_wrap)] fn input(_: Option>) {} diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 8ae1d23a8e3..8ace8338fcf 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,5 +1,5 @@ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:3:13 + --> $DIR/option_option.rs:4:13 | LL | fn input(_: Option>) {} | ^^^^^^^^^^^^^^^^^^ @@ -11,55 +11,55 @@ LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:5:16 + --> $DIR/option_option.rs:6:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:9:27 + --> $DIR/option_option.rs:10:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:14:30 + --> $DIR/option_option.rs:15:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:19:8 + --> $DIR/option_option.rs:20:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:23:23 + --> $DIR/option_option.rs:24:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:29:22 + --> $DIR/option_option.rs:30:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:33:11 + --> $DIR/option_option.rs:34:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:34:17 + --> $DIR/option_option.rs:35:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:75:14 + --> $DIR/option_option.rs:78:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 20e5016bc17..e1735bc88f5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -2,6 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] +#![allow(clippy::unnecessary_wrap)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index e7192deeebb..a6abd2e8b34 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -2,6 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] +#![allow(clippy::unnecessary_wrap)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index d0c4df0e008..8a7b5574c84 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:32:19 + --> $DIR/or_fun_call.rs:33:19 | LL | with_const_fn.unwrap_or(Duration::from_secs(5)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))` @@ -7,55 +7,55 @@ LL | with_const_fn.unwrap_or(Duration::from_secs(5)); = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:35:22 + --> $DIR/or_fun_call.rs:36:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:38:5 + --> $DIR/or_fun_call.rs:39:5 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:41:21 + --> $DIR/or_fun_call.rs:42:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:44:14 + --> $DIR/or_fun_call.rs:45:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:47:19 + --> $DIR/or_fun_call.rs:48:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:50:5 + --> $DIR/or_fun_call.rs:51:5 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:53:5 + --> $DIR/or_fun_call.rs:54:5 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:56:5 + --> $DIR/or_fun_call.rs:57:5 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:59:21 + --> $DIR/or_fun_call.rs:60:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs index 287726f7a2d..be4e85d05a7 100644 --- a/tests/ui/panic_in_result_fn.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -1,4 +1,5 @@ #![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wrap)] struct A; diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index c6936fd8692..ca73ac5a411 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,5 +1,5 @@ error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:6:5 + --> $DIR/panic_in_result_fn.rs:7:5 | LL | / fn result_with_panic() -> Result // should emit lint LL | | { @@ -10,14 +10,14 @@ LL | | } = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:8:9 + --> $DIR/panic_in_result_fn.rs:9:9 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:11:5 + --> $DIR/panic_in_result_fn.rs:12:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint LL | | { @@ -27,14 +27,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:13:9 + --> $DIR/panic_in_result_fn.rs:14:9 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:16:5 + --> $DIR/panic_in_result_fn.rs:17:5 | LL | / fn result_with_unreachable() -> Result // should emit lint LL | | { @@ -44,14 +44,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:18:9 + --> $DIR/panic_in_result_fn.rs:19:9 | LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:21:5 + --> $DIR/panic_in_result_fn.rs:22:5 | LL | / fn result_with_todo() -> Result // should emit lint LL | | { @@ -61,14 +61,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:23:9 + --> $DIR/panic_in_result_fn.rs:24:9 | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:52:1 + --> $DIR/panic_in_result_fn.rs:53:1 | LL | / fn function_result_with_panic() -> Result // should emit lint LL | | { @@ -78,14 +78,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:54:5 + --> $DIR/panic_in_result_fn.rs:55:5 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:67:1 + --> $DIR/panic_in_result_fn.rs:68:1 | LL | / fn main() -> Result<(), String> { LL | | todo!("finish main method"); @@ -95,7 +95,7 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:68:5 + --> $DIR/panic_in_result_fn.rs:69:5 | LL | todo!("finish main method"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 11dff94a288..df3c98a0aa5 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,5 +1,6 @@ // run-rustfix #![allow(unreachable_code)] +#![allow(clippy::unnecessary_wrap)] fn some_func(a: Option) -> Option { a?; diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 1d0ee82b4f7..62b3e96b65a 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,5 +1,6 @@ // run-rustfix #![allow(unreachable_code)] +#![allow(clippy::unnecessary_wrap)] fn some_func(a: Option) -> Option { if a.is_none() { diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 502615fb175..6f330cfa385 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:5:5 + --> $DIR/question_mark.rs:6:5 | LL | / if a.is_none() { LL | | return None; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:50:9 + --> $DIR/question_mark.rs:51:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:54:9 + --> $DIR/question_mark.rs:55:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:58:17 + --> $DIR/question_mark.rs:59:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:64:17 + --> $DIR/question_mark.rs:65:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:81:9 + --> $DIR/question_mark.rs:82:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:89:9 + --> $DIR/question_mark.rs:90:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:97:9 + --> $DIR/question_mark.rs:98:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:104:26 + --> $DIR/question_mark.rs:105:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:114:17 + --> $DIR/question_mark.rs:115:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:129:5 + --> $DIR/question_mark.rs:130:5 | LL | / if f().is_none() { LL | | return None; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index fe8f62503b7..2e7ba8bc04d 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -7,6 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::unnecessary_wrap, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index 09426a6e590..ea0e18f5970 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -7,6 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::unnecessary_wrap, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 3473ceea00e..5341e81836a 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:15:12 + --> $DIR/redundant_pattern_matching.rs:16:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,13 +7,13 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:17:12 + --> $DIR/redundant_pattern_matching.rs:18:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:19:12 + --> $DIR/redundant_pattern_matching.rs:20:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index aa43e69f79e..19458ddf68e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] +#![allow(clippy::unnecessary_wrap)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index df3a9dc5367..ab6a32dbe4d 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] +#![allow(clippy::unnecessary_wrap)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 3905ed2476b..ababa64e6d8 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:18:9 + --> $DIR/try_err.rs:19:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` @@ -11,19 +11,19 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:28:9 + --> $DIR/try_err.rs:29:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:48:17 + --> $DIR/try_err.rs:49:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:67:17 + --> $DIR/try_err.rs:68:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index fec115ff29d..6bf704c09e0 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -4,6 +4,7 @@ unused_must_use, unused_variables, clippy::unused_unit, + clippy::unnecessary_wrap, clippy::or_fun_call )] diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 90fee3aab23..c3a839a9bf8 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:29:5 + --> $DIR/unit_arg.rs:30:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:33:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:34:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:38:5 + --> $DIR/unit_arg.rs:39:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:41:5 + --> $DIR/unit_arg.rs:42:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:43:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:46:5 + --> $DIR/unit_arg.rs:47:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:57:13 + --> $DIR/unit_arg.rs:58:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:60:5 + --> $DIR/unit_arg.rs:61:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:93:5 + --> $DIR/unit_arg.rs:94:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index e785ac02feb..3b1a6cf57c6 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wrap)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 8a9b0cd3cf0..45ee367d649 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] +#![allow(clippy::unnecessary_wrap)] fn test_generic(val: T) -> T { let _ = val; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 4faa1572973..e5bdffed20d 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] +#![allow(clippy::unnecessary_wrap)] fn test_generic(val: T) -> T { let _ = T::from(val); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 11c6efb25cc..26a33595031 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion.rs:6:13 + --> $DIR/useless_conversion.rs:7:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` @@ -11,61 +11,61 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion.rs:7:5 + --> $DIR/useless_conversion.rs:8:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:19:22 + --> $DIR/useless_conversion.rs:20:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:60:21 + --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:61:21 + --> $DIR/useless_conversion.rs:62:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:62:13 + --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:63:13 + --> $DIR/useless_conversion.rs:64:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines` - --> $DIR/useless_conversion.rs:64:13 + --> $DIR/useless_conversion.rs:65:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:65:13 + --> $DIR/useless_conversion.rs:66:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:66:21 + --> $DIR/useless_conversion.rs:67:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:71:13 + --> $DIR/useless_conversion.rs:72:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 287f8935327..14007e5f251 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -4,6 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 1f261159f4a..0e8631ca704 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -4,6 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 351988f31ea..66267dd27b8 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:11:5 + --> $DIR/wildcard_imports.rs:12:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:12:5 + --> $DIR/wildcard_imports.rs:13:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:13:5 + --> $DIR/wildcard_imports.rs:14:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:15:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:19:5 + --> $DIR/wildcard_imports.rs:20:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:20:5 + --> $DIR/wildcard_imports.rs:21:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:91:13 + --> $DIR/wildcard_imports.rs:92:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:75 + --> $DIR/wildcard_imports.rs:98:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:98:13 + --> $DIR/wildcard_imports.rs:99:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:109:20 + --> $DIR/wildcard_imports.rs:110:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:109:30 + --> $DIR/wildcard_imports.rs:110:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:116:13 + --> $DIR/wildcard_imports.rs:117:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:145:9 + --> $DIR/wildcard_imports.rs:146:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:154:9 + --> $DIR/wildcard_imports.rs:155:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:155:9 + --> $DIR/wildcard_imports.rs:156:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,31 +93,31 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:166:13 + --> $DIR/wildcard_imports.rs:167:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:201:17 + --> $DIR/wildcard_imports.rs:202:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:209:13 + --> $DIR/wildcard_imports.rs:210:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:218:17 + --> $DIR/wildcard_imports.rs:219:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:227:13 + --> $DIR/wildcard_imports.rs:228:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` From e998d61fe6a1407f73504a2fee16df2f9167a37c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 18:36:37 +0900 Subject: [PATCH 0962/1110] Downgrade applicability to MaybeIncorrect --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 28cc20f0007..6434b3033dd 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { fn_decl.output.span(), format!("remove `{}` from the return type...", return_type).as_str(), inner_ty, - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); }); diag.multipart_suggestion( From 9d96311d7354d99b6f3b3956405b7defac81461c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 18:46:16 +0900 Subject: [PATCH 0963/1110] Remove wildcard use --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 6434b3033dd..7a9cf627059 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -5,7 +5,7 @@ use crate::utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::*; +use rustc_hir::{Body, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; From 532d205218c7aa28c1718f9e9088eeced6e2c5ac Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 19:24:11 +0900 Subject: [PATCH 0964/1110] Skip functions in PartialOrd --- clippy_lints/src/unnecessary_wrap.rs | 12 ++++++++++-- clippy_lints/src/utils/paths.rs | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 7a9cf627059..ec6c823a4ec 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,6 +1,6 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, + in_macro, is_type_diagnostic_item, match_path, match_qpath, paths, return_ty, snippet, span_lint_and_then, + trait_ref_of_method, visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -63,6 +63,14 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { + if_chain! { + if let Some(trait_ref) = trait_ref_of_method(cx, hir_id); + if match_path(trait_ref.path, &paths::PARTIAL_ORD); + then { + return; + } + } + match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2be5ff93f86..97e01f445ff 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -81,6 +81,7 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; +pub const PARTIAL_ORD: [&str; 3] = ["std", "cmp", "PartialOrd"]; pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; From bf46f78ca7e15631f08253c14975896030ca24a8 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 19:35:59 +0900 Subject: [PATCH 0965/1110] Fix clippy error --- clippy_lints/src/redundant_clone.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index b4a9804fb25..c4ca7fcdecc 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -320,11 +320,11 @@ fn find_stmt_assigns_to<'tcx>( match (by_ref, &*rvalue) { (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => { - base_local_and_movability(cx, mir, *place) + Some(base_local_and_movability(cx, mir, *place)) }, (false, mir::Rvalue::Ref(_, _, place)) => { if let [mir::ProjectionElem::Deref] = place.as_ref().projection { - base_local_and_movability(cx, mir, *place) + Some(base_local_and_movability(cx, mir, *place)) } else { None } @@ -341,7 +341,7 @@ fn base_local_and_movability<'tcx>( cx: &LateContext<'tcx>, mir: &mir::Body<'tcx>, place: mir::Place<'tcx>, -) -> Option<(mir::Local, CannotMoveOut)> { +) -> (mir::Local, CannotMoveOut) { use rustc_middle::mir::PlaceRef; // Dereference. You cannot move things out from a borrowed value. @@ -362,7 +362,7 @@ fn base_local_and_movability<'tcx>( && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty); } - Some((local, deref || field || slice)) + (local, deref || field || slice) } struct LocalUseVisitor { From 86331a46e4030ddea41e1f9f9b8c313f58ce1eb1 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 2 Nov 2020 23:59:47 +0900 Subject: [PATCH 0966/1110] Update stderr files --- tests/ui/doc_errors.stderr | 14 ++++---- tests/ui/drop_ref.stderr | 36 ++++++++++---------- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_err.rs | 1 + tests/ui/map_err.stderr | 2 +- tests/ui/or_fun_call.stderr | 6 ++-- tests/ui/redundant_pattern_matching.stderr | 38 +++++++++++----------- tests/ui/result_unit_error.rs | 1 + tests/ui/result_unit_error.stderr | 8 ++--- 10 files changed, 56 insertions(+), 54 deletions(-) diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index c7b616e2897..b5a81419dae 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:7:1 + --> $DIR/doc_errors.rs:8:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:11:1 + --> $DIR/doc_errors.rs:12:1 | LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:16:1 + --> $DIR/doc_errors.rs:17:1 | LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:21:1 + --> $DIR/doc_errors.rs:22:1 | LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:51:5 + --> $DIR/doc_errors.rs:52:5 | LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:56:5 + --> $DIR/doc_errors.rs:57:5 | LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -49,7 +49,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:85:5 + --> $DIR/doc_errors.rs:86:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 7974bf56d44..10087cb4820 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:10:5 + --> $DIR/drop_ref.rs:11:5 | LL | drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::drop-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:10:10 + --> $DIR/drop_ref.rs:11:10 | LL | drop(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:13:5 + --> $DIR/drop_ref.rs:14:5 | LL | drop(&owned1); | ^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:13:10 + --> $DIR/drop_ref.rs:14:10 | LL | drop(&owned1); | ^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:14:5 + --> $DIR/drop_ref.rs:15:5 | LL | drop(&&owned1); | ^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/drop_ref.rs:14:10 + --> $DIR/drop_ref.rs:15:10 | LL | drop(&&owned1); | ^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:15:5 + --> $DIR/drop_ref.rs:16:5 | LL | drop(&mut owned1); | ^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:15:10 + --> $DIR/drop_ref.rs:16:10 | LL | drop(&mut owned1); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:19:5 + --> $DIR/drop_ref.rs:20:5 | LL | drop(reference1); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:19:10 + --> $DIR/drop_ref.rs:20:10 | LL | drop(reference1); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:22:5 + --> $DIR/drop_ref.rs:23:5 | LL | drop(reference2); | ^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:22:10 + --> $DIR/drop_ref.rs:23:10 | LL | drop(reference2); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:25:5 + --> $DIR/drop_ref.rs:26:5 | LL | drop(reference3); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:25:10 + --> $DIR/drop_ref.rs:26:10 | LL | drop(reference3); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:30:5 + --> $DIR/drop_ref.rs:31:5 | LL | drop(&val); | ^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/drop_ref.rs:30:10 + --> $DIR/drop_ref.rs:31:10 | LL | drop(&val); | ^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:38:5 + --> $DIR/drop_ref.rs:39:5 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:38:20 + --> $DIR/drop_ref.rs:39:20 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 5aa5a43cb92..740b2f66728 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables)] +#![allow(unused_variables, clippy::unnecessary_wrap)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index df534031f54..6750662c58c 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables)] +#![allow(unused_variables, clippy::unnecessary_wrap)] fn option_unwrap_or() { // int case diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 617b6422872..231562507a8 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,4 +1,5 @@ #![warn(clippy::map_err_ignore)] +#![allow(clippy::unnecessary_wrap)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 7273f460380..390d7ce2e4e 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` ignores the original error - --> $DIR/map_err.rs:22:32 + --> $DIR/map_err.rs:23:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 8a7b5574c84..a29e8fb58f5 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -61,19 +61,19 @@ LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 + --> $DIR/or_fun_call.rs:63:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 + --> $DIR/or_fun_call.rs:66:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:69:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 5341e81836a..aeb309f5ba1 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -19,19 +19,19 @@ LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:21:15 + --> $DIR/redundant_pattern_matching.rs:22:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:23:15 + --> $DIR/redundant_pattern_matching.rs:24:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:33:5 + --> $DIR/redundant_pattern_matching.rs:34:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:38:5 + --> $DIR/redundant_pattern_matching.rs:39:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:43:5 + --> $DIR/redundant_pattern_matching.rs:44:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:48:5 + --> $DIR/redundant_pattern_matching.rs:49:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:53:20 + --> $DIR/redundant_pattern_matching.rs:54:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:59:20 + --> $DIR/redundant_pattern_matching.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:61:19 + --> $DIR/redundant_pattern_matching.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:84:19 + --> $DIR/redundant_pattern_matching.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:85:16 + --> $DIR/redundant_pattern_matching.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:91:12 + --> $DIR/redundant_pattern_matching.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:92:15 + --> $DIR/redundant_pattern_matching.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:110:12 + --> $DIR/redundant_pattern_matching.rs:111:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:112:12 + --> $DIR/redundant_pattern_matching.rs:113:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:114:15 + --> $DIR/redundant_pattern_matching.rs:115:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:116:15 + --> $DIR/redundant_pattern_matching.rs:117:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:118:5 + --> $DIR/redundant_pattern_matching.rs:119:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:123:5 + --> $DIR/redundant_pattern_matching.rs:124:5 | LL | / match Err::(42) { LL | | Ok(_) => false, diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index a66f581b215..1b4a702377e 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,3 +1,4 @@ +#![allow(clippy::unnecessary_wrap)] #[warn(clippy::result_unit_err)] #[allow(unused)] diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index b8230032491..12901b354f9 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,5 +1,5 @@ error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:4:1 + --> $DIR/result_unit_error.rs:5:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn returns_unit_error() -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:13:5 + --> $DIR/result_unit_error.rs:14:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn get_that_error(&self) -> Result; = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:15:5 + --> $DIR/result_unit_error.rs:16:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | fn get_this_one_too(&self) -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:33:5 + --> $DIR/result_unit_error.rs:34:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 4c8d248190819f09753d4f6a9f89e3804e232ae7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 18:54:16 +0900 Subject: [PATCH 0967/1110] Update stderr files --- tests/ui/map_flatten.stderr | 12 ++++++------ tests/ui/option_option.stderr | 2 +- tests/ui/or_fun_call.stderr | 8 ++++---- tests/ui/try_err.stderr | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index d4e27f9aa07..756e6e818ad 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:15:46 + --> $DIR/map_flatten.rs:16:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` @@ -7,31 +7,31 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:16:46 + --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:17:46 + --> $DIR/map_flatten.rs:18:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:18:46 + --> $DIR/map_flatten.rs:19:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:21:46 + --> $DIR/map_flatten.rs:22:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:24:39 + --> $DIR/map_flatten.rs:25:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 8ace8338fcf..ad7f081c713 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -59,7 +59,7 @@ LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:78:14 + --> $DIR/option_option.rs:76:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index a29e8fb58f5..fb8bf339828 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -79,25 +79,25 @@ LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:76:21 + --> $DIR/or_fun_call.rs:77:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:78:21 + --> $DIR/or_fun_call.rs:79:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:102:35 + --> $DIR/or_fun_call.rs:103:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:106:10 + --> $DIR/or_fun_call.rs:107:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index ababa64e6d8..2c01d37192e 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -29,7 +29,7 @@ LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:86:23 + --> $DIR/try_err.rs:87:23 | LL | Err(_) => Err(1)?, | ^^^^^^^ help: try this: `return Err(1)` @@ -40,7 +40,7 @@ LL | try_validation!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:101:23 + --> $DIR/try_err.rs:102:23 | LL | Err(_) => Err(ret_one!())?, | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` @@ -51,25 +51,25 @@ LL | try_validation_in_macro!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:140:9 + --> $DIR/try_err.rs:141:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:147:9 + --> $DIR/try_err.rs:148:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:149:9 + --> $DIR/try_err.rs:150:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:157:9 + --> $DIR/try_err.rs:158:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` From 4e5c02e8980d16feeee953f112f940c598180ddc Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 19:25:54 +0900 Subject: [PATCH 0968/1110] Ignore trait implementations --- clippy_lints/src/unnecessary_wrap.rs | 20 +++++++++----------- clippy_lints/src/utils/paths.rs | 1 - tests/ui/unnecessary_wrap.rs | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index ec6c823a4ec..2960ffc5352 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,11 +1,11 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_path, match_qpath, paths, return_ty, snippet, span_lint_and_then, - trait_ref_of_method, visitors::find_all_ret_expressions, + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -63,14 +63,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { - if_chain! { - if let Some(trait_ref) = trait_ref_of_method(cx, hir_id); - if match_path(trait_ref.path, &paths::PARTIAL_ORD); - then { - return; - } - } - match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { @@ -81,6 +73,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { _ => (), } + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + return; + } + } + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { ("Option", &paths::OPTION_SOME) } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 97e01f445ff..2be5ff93f86 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -81,7 +81,6 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; -pub const PARTIAL_ORD: [&str; 3] = ["std", "cmp", "PartialOrd"]; pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 618c452065b..11208690428 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -95,6 +95,20 @@ impl A { } } +trait B { + // trait impls are not linted + fn func13() -> Option { + Some(1) + } +} + +impl A for B { + // trait impls are not linted + fn func13() -> Option { + Some(0) + } +} + fn main() { // method calls are not linted func1(true, true); From 1f577c030049d974b7982f46e90d2bf96e665ea1 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 19:39:41 +0900 Subject: [PATCH 0969/1110] Fix embarrassing grammatical error --- tests/ui/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 11208690428..58178e2e04b 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -102,7 +102,7 @@ trait B { } } -impl A for B { +impl B for A { // trait impls are not linted fn func13() -> Option { Some(0) From c7445d7f2c3eece2b9056d05ea92fb1d1112b3ca Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 18 Nov 2020 01:01:22 +0900 Subject: [PATCH 0970/1110] Pluralize lint name --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +++++----- .../{unnecessary_wrap.rs => unnecessary_wraps.rs} | 8 ++++---- src/lintlist/mod.rs | 4 ++-- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- tests/ui/doc_errors.rs | 2 +- tests/ui/drop_ref.rs | 2 +- tests/ui/forget_ref.rs | 2 +- tests/ui/let_underscore_must_use.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_err.rs | 2 +- tests/ui/map_flatten.fixed | 2 +- tests/ui/map_flatten.rs | 2 +- tests/ui/needless_lifetimes.rs | 2 +- tests/ui/option_map_unit_fn_fixable.fixed | 2 +- tests/ui/option_map_unit_fn_fixable.rs | 2 +- tests/ui/option_option.rs | 2 +- tests/ui/or_fun_call.fixed | 2 +- tests/ui/or_fun_call.rs | 2 +- tests/ui/panic_in_result_fn.rs | 2 +- tests/ui/question_mark.fixed | 2 +- tests/ui/question_mark.rs | 2 +- tests/ui/redundant_pattern_matching.fixed | 2 +- tests/ui/redundant_pattern_matching.rs | 2 +- tests/ui/result_unit_error.rs | 2 +- tests/ui/try_err.fixed | 2 +- tests/ui/try_err.rs | 2 +- tests/ui/unit_arg.rs | 2 +- tests/ui/unnecessary_clone.rs | 2 +- .../ui/{unnecessary_wrap.rs => unnecessary_wraps.rs} | 2 +- ...ecessary_wrap.stderr => unnecessary_wraps.stderr} | 12 ++++++------ tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- tests/ui/wildcard_imports.fixed | 2 +- tests/ui/wildcard_imports.rs | 2 +- 36 files changed, 49 insertions(+), 49 deletions(-) rename clippy_lints/src/{unnecessary_wrap.rs => unnecessary_wraps.rs} (96%) rename tests/ui/{unnecessary_wrap.rs => unnecessary_wraps.rs} (97%) rename tests/ui/{unnecessary_wrap.stderr => unnecessary_wraps.stderr} (89%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b862d3196..64f67680b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2008,7 +2008,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap -[`unnecessary_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wrap +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2d1f75391bb..f0c1cb8d6e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,7 +323,7 @@ mod unicode; mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; -mod unnecessary_wrap; +mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -893,7 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, - &unnecessary_wrap::UNNECESSARY_WRAP, + &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, @@ -1066,7 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(|| box unnecessary_wrap::UnnecessaryWrap); + store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1574,7 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1779,7 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wraps.rs similarity index 96% rename from clippy_lints/src/unnecessary_wrap.rs rename to clippy_lints/src/unnecessary_wraps.rs index 2960ffc5352..25ecc7a82f1 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -46,14 +46,14 @@ declare_clippy_lint! { /// } /// } /// ``` - pub UNNECESSARY_WRAP, + pub UNNECESSARY_WRAPS, complexity, "functions that only return `Ok` or `Some`" } -declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]); +declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); -impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { if can_sugg && !suggs.is_empty() { span_lint_and_then( cx, - UNNECESSARY_WRAP, + UNNECESSARY_WRAPS, span, format!( "this function's return value is unnecessarily wrapped by `{}`", diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4a0cdc5d82f..a2edd6cd0bd 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2609,11 +2609,11 @@ vec![ module: "unwrap", }, Lint { - name: "unnecessary_wrap", + name: "unnecessary_wraps", group: "complexity", desc: "functions that only return `Ok` or `Some`", deprecation: None, - module: "unnecessary_wrap", + module: "unnecessary_wraps", }, Lint { name: "unneeded_field_pattern", diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 78ec1727fc1..6f12d36d777 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ #![warn(clippy::derive_ord_xor_partial_ord)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::cmp::Ordering; diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 77df7f176f0..c77a74a58f2 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,7 +1,7 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] #![allow(clippy::result_unit_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::io; diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index ba12e763821..e1a15c609fd 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,7 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::mem::drop; diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index a9c2a92ce6b..c49e6756a6c 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::mem::forget; diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index e3800cda1b1..a842e872a37 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -1,5 +1,5 @@ #![warn(clippy::let_underscore_must_use)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] // Debug implementations can fire this lint, // so we shouldn't lint external macros diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 740b2f66728..81d903c15d3 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wrap)] +#![allow(unused_variables, clippy::unnecessary_wraps)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 6750662c58c..16105d379c3 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wrap)] +#![allow(unused_variables, clippy::unnecessary_wraps)] fn option_unwrap_or() { // int case diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 231562507a8..05b9949f102 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,5 +1,5 @@ #![warn(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index b4a51837b2f..773b5914439 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -4,7 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index e83cc46bda2..578bd877267 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -4,7 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index e5973bbef8d..44972c8c639 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_lifetimes)] -#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wrap)] +#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps)] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index de2e9155906..7d29445e66c 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -2,7 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index f0887c8a4bc..b6f834f686f 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -2,7 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 557d29dff67..6859ba8e5bb 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,5 +1,5 @@ #![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn input(_: Option>) {} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index e1735bc88f5..2a63318c8c7 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index a6abd2e8b34..026ef437caa 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs index be4e85d05a7..3d3c19a1be5 100644 --- a/tests/ui/panic_in_result_fn.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] struct A; diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index df3c98a0aa5..0b5746cb522 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(unreachable_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn some_func(a: Option) -> Option { a?; diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 62b3e96b65a..0f0825c9334 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(unreachable_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn some_func(a: Option) -> Option { if a.is_none() { diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 2e7ba8bc04d..aa20512296a 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -7,7 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index ea0e18f5970..d76f9c288ff 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -7,7 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, deprecated )] diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index 1b4a702377e..5e57c752b5a 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[warn(clippy::result_unit_err)] #[allow(unused)] diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 19458ddf68e..652b611208b 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index ab6a32dbe4d..6bd479657b7 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 6bf704c09e0..9ad16d36509 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -4,7 +4,7 @@ unused_must_use, unused_variables, clippy::unused_unit, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, clippy::or_fun_call )] diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 3b1a6cf57c6..6770a7fac90 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wrap)] +#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wraps.rs similarity index 97% rename from tests/ui/unnecessary_wrap.rs rename to tests/ui/unnecessary_wraps.rs index 58178e2e04b..a53dec8f91a 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -1,4 +1,4 @@ -#![warn(clippy::unnecessary_wrap)] +#![warn(clippy::unnecessary_wraps)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wraps.stderr similarity index 89% rename from tests/ui/unnecessary_wrap.stderr rename to tests/ui/unnecessary_wraps.stderr index 5f21b74bc76..410f054b8ef 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -1,5 +1,5 @@ error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:8:1 + --> $DIR/unnecessary_wraps.rs:8:1 | LL | / fn func1(a: bool, b: bool) -> Option { LL | | if a && b { @@ -10,7 +10,7 @@ LL | | } LL | | } | |_^ | - = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` + = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { @@ -26,7 +26,7 @@ LL | } else { ... error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:21:1 + --> $DIR/unnecessary_wraps.rs:21:1 | LL | / fn func2(a: bool, b: bool) -> Option { LL | | if a && b { @@ -52,7 +52,7 @@ LL | 30 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:51:1 + --> $DIR/unnecessary_wraps.rs:51:1 | LL | / fn func5() -> Option { LL | | Some(1) @@ -69,7 +69,7 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wrap.rs:61:1 + --> $DIR/unnecessary_wraps.rs:61:1 | LL | / fn func7() -> Result { LL | | Ok(1) @@ -86,7 +86,7 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:93:5 + --> $DIR/unnecessary_wraps.rs:93:5 | LL | / fn func12() -> Option { LL | | Some(1) diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 45ee367d649..03977de9455 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,7 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn test_generic(val: T) -> T { let _ = val; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index e5bdffed20d..f6e094c1661 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,7 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn test_generic(val: T) -> T { let _ = T::from(val); diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 14007e5f251..ee9c9045fff 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -4,7 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 0e8631ca704..efaa8f9ef66 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -4,7 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #![warn(unused_imports)] extern crate wildcard_imports_helper; From bf2d31d0533ee03a2c1e91f74780ea779e8b330c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 17 Nov 2020 18:08:12 +0100 Subject: [PATCH 0971/1110] Run cargo dev fmt --- clippy_lints/src/mut_key.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 9cceecc785a..11044e0c2fb 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -89,11 +89,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir:: for (hir_ty, ty) in decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) { check_ty(cx, hir_ty.span, ty); } - check_ty( - cx, - decl.output.span(), - cx.tcx.erase_late_bound_regions(fn_sig.output()), - ); + check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); } // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased From 5a8396887714fb75f44eae2a3775b1b2a12f38ae Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Tue, 17 Nov 2020 22:34:39 +0100 Subject: [PATCH 0972/1110] Change `redundant_pattern_matching` to also lint `std::task::Poll` Suggest using utility methods `is_pending` and `is_ready`. --- clippy_lints/src/matches.rs | 33 ++++- clippy_lints/src/utils/paths.rs | 2 + .../redundant_pattern_matching_option.fixed | 8 +- tests/ui/redundant_pattern_matching_option.rs | 8 +- .../redundant_pattern_matching_option.stderr | 38 +++--- .../ui/redundant_pattern_matching_poll.fixed | 73 ++++++++++ tests/ui/redundant_pattern_matching_poll.rs | 88 ++++++++++++ .../ui/redundant_pattern_matching_poll.stderr | 128 ++++++++++++++++++ ...> redundant_pattern_matching_result.fixed} | 1 - ...s => redundant_pattern_matching_result.rs} | 1 - ... redundant_pattern_matching_result.stderr} | 44 +++--- 11 files changed, 364 insertions(+), 60 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_poll.fixed create mode 100644 tests/ui/redundant_pattern_matching_poll.rs create mode 100644 tests/ui/redundant_pattern_matching_poll.stderr rename tests/ui/{redundant_pattern_matching.fixed => redundant_pattern_matching_result.fixed} (98%) rename tests/ui/{redundant_pattern_matching.rs => redundant_pattern_matching_result.rs} (99%) rename tests/ui/{redundant_pattern_matching.stderr => redundant_pattern_matching_result.stderr} (80%) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c6dca54e250..af59917e801 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -411,8 +411,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result` or - /// `Option` + /// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or + /// `std::task::Poll` /// /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function @@ -422,10 +422,13 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// # use std::task::Poll; /// if let Ok(_) = Ok::(42) {} /// if let Err(_) = Err::(42) {} /// if let None = None::<()> {} /// if let Some(_) = Some(42) {} + /// if let Poll::Pending = Poll::Pending::<()> {} + /// if let Poll::Ready(_) = Poll::Ready(42) {} /// match Ok::(42) { /// Ok(_) => true, /// Err(_) => false, @@ -435,10 +438,13 @@ declare_clippy_lint! { /// The more idiomatic use would be: /// /// ```rust + /// # use std::task::Poll; /// if Ok::(42).is_ok() {} /// if Err::(42).is_err() {} /// if None::<()>.is_none() {} /// if Some(42).is_some() {} + /// if Poll::Pending::<()>.is_pending() {} + /// if Poll::Ready(42).is_ready() {} /// Ok::(42).is_ok(); /// ``` pub REDUNDANT_PATTERN_MATCHING, @@ -1538,6 +1544,8 @@ mod redundant_pattern_match { "is_err()" } else if match_qpath(path, &paths::OPTION_SOME) { "is_some()" + } else if match_qpath(path, &paths::POLL_READY) { + "is_ready()" } else { return; } @@ -1545,7 +1553,15 @@ mod redundant_pattern_match { return; } }, - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", + PatKind::Path(ref path) => { + if match_qpath(path, &paths::OPTION_NONE) { + "is_none()" + } else if match_qpath(path, &paths::POLL_PENDING) { + "is_pending()" + } else { + return; + } + }, _ => return, }; @@ -1628,6 +1644,17 @@ mod redundant_pattern_match { "is_some()", "is_none()", ) + .or_else(|| { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::POLL_READY, + &paths::POLL_PENDING, + "is_ready()", + "is_pending()", + ) + }) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2be5ff93f86..e20f146f145 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -86,6 +86,8 @@ pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; +pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; +pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 499b975b2bb..bc369dd2491 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,13 +2,7 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - deprecated -)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 2a98435e790..d7616a72913 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,13 +2,7 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - deprecated -)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index eebb3448491..7ddfbe503a2 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:14:12 + --> $DIR/redundant_pattern_matching_option.rs:8:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` @@ -7,43 +7,43 @@ LL | if let None = None::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:16:12 + --> $DIR/redundant_pattern_matching_option.rs:10:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:18:12 + --> $DIR/redundant_pattern_matching_option.rs:12:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:24:15 + --> $DIR/redundant_pattern_matching_option.rs:18:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:26:15 + --> $DIR/redundant_pattern_matching_option.rs:20:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:28:15 + --> $DIR/redundant_pattern_matching_option.rs:22:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:31:15 + --> $DIR/redundant_pattern_matching_option.rs:25:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:39:5 + --> $DIR/redundant_pattern_matching_option.rs:33:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:44:5 + --> $DIR/redundant_pattern_matching_option.rs:38:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:49:13 + --> $DIR/redundant_pattern_matching_option.rs:43:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,49 +71,49 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:55:20 + --> $DIR/redundant_pattern_matching_option.rs:49:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:60:20 + --> $DIR/redundant_pattern_matching_option.rs:54:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:62:19 + --> $DIR/redundant_pattern_matching_option.rs:56:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:83:12 + --> $DIR/redundant_pattern_matching_option.rs:77:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:85:12 + --> $DIR/redundant_pattern_matching_option.rs:79:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:87:15 + --> $DIR/redundant_pattern_matching_option.rs:81:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:89:15 + --> $DIR/redundant_pattern_matching_option.rs:83:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:91:5 + --> $DIR/redundant_pattern_matching_option.rs:85:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:96:5 + --> $DIR/redundant_pattern_matching_option.rs:90:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed new file mode 100644 index 00000000000..564c427f063 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::task::Poll::{self, Pending, Ready}; + +fn main() { + if Pending::<()>.is_pending() {} + + if Ready(42).is_ready() {} + + if Ready(42).is_ready() { + foo(); + } else { + bar(); + } + + while Ready(42).is_ready() {} + + while Ready(42).is_pending() {} + + while Pending::<()>.is_pending() {} + + if Pending::.is_pending() {} + + if Ready(42).is_ready() {} + + Ready(42).is_ready(); + + Pending::<()>.is_pending(); + + let _ = Pending::<()>.is_pending(); + + let poll = Ready(false); + let x = if poll.is_ready() { true } else { false }; + takes_poll(x); + + poll_const(); + + let _ = if gen_poll().is_ready() { + 1 + } else if gen_poll().is_pending() { + 2 + } else { + 3 + }; +} + +fn gen_poll() -> Poll<()> { + Pending +} + +fn takes_poll(_: bool) {} + +fn foo() {} + +fn bar() {} + +const fn poll_const() { + if Ready(42).is_ready() {} + + if Pending::<()>.is_pending() {} + + while Ready(42).is_ready() {} + + while Pending::<()>.is_pending() {} + + Ready(42).is_ready(); + + Pending::<()>.is_pending(); +} diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs new file mode 100644 index 00000000000..d453d4184af --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -0,0 +1,88 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::task::Poll::{self, Pending, Ready}; + +fn main() { + if let Pending = Pending::<()> {} + + if let Ready(_) = Ready(42) {} + + if let Ready(_) = Ready(42) { + foo(); + } else { + bar(); + } + + while let Ready(_) = Ready(42) {} + + while let Pending = Ready(42) {} + + while let Pending = Pending::<()> {} + + if Pending::.is_pending() {} + + if Ready(42).is_ready() {} + + match Ready(42) { + Ready(_) => true, + Pending => false, + }; + + match Pending::<()> { + Ready(_) => false, + Pending => true, + }; + + let _ = match Pending::<()> { + Ready(_) => false, + Pending => true, + }; + + let poll = Ready(false); + let x = if let Ready(_) = poll { true } else { false }; + takes_poll(x); + + poll_const(); + + let _ = if let Ready(_) = gen_poll() { + 1 + } else if let Pending = gen_poll() { + 2 + } else { + 3 + }; +} + +fn gen_poll() -> Poll<()> { + Pending +} + +fn takes_poll(_: bool) {} + +fn foo() {} + +fn bar() {} + +const fn poll_const() { + if let Ready(_) = Ready(42) {} + + if let Pending = Pending::<()> {} + + while let Ready(_) = Ready(42) {} + + while let Pending = Pending::<()> {} + + match Ready(42) { + Ready(_) => true, + Pending => false, + }; + + match Pending::<()> { + Ready(_) => false, + Pending => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr new file mode 100644 index 00000000000..42e5d6f41fe --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -0,0 +1,128 @@ +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:10:12 + | +LL | if let Pending = Pending::<()> {} + | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:12:12 + | +LL | if let Ready(_) = Ready(42) {} + | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:14:12 + | +LL | if let Ready(_) = Ready(42) { + | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:20:15 + | +LL | while let Ready(_) = Ready(42) {} + | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:22:15 + | +LL | while let Pending = Ready(42) {} + | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:24:15 + | +LL | while let Pending = Pending::<()> {} + | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:30:5 + | +LL | / match Ready(42) { +LL | | Ready(_) => true, +LL | | Pending => false, +LL | | }; + | |_____^ help: try this: `Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:35:5 + | +LL | / match Pending::<()> { +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:40:13 + | +LL | let _ = match Pending::<()> { + | _____________^ +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:46:20 + | +LL | let x = if let Ready(_) = poll { true } else { false }; + | -------^^^^^^^^------- help: try this: `if poll.is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:51:20 + | +LL | let _ = if let Ready(_) = gen_poll() { + | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:53:19 + | +LL | } else if let Pending = gen_poll() { + | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:71:12 + | +LL | if let Ready(_) = Ready(42) {} + | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:73:12 + | +LL | if let Pending = Pending::<()> {} + | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:75:15 + | +LL | while let Ready(_) = Ready(42) {} + | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:77:15 + | +LL | while let Pending = Pending::<()> {} + | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:79:5 + | +LL | / match Ready(42) { +LL | | Ready(_) => true, +LL | | Pending => false, +LL | | }; + | |_____^ help: try this: `Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:84:5 + | +LL | / match Pending::<()> { +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching_result.fixed similarity index 98% rename from tests/ui/redundant_pattern_matching.fixed rename to tests/ui/redundant_pattern_matching_result.fixed index aa20512296a..e94c5704b48 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -3,7 +3,6 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow( - clippy::unit_arg, unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching_result.rs similarity index 99% rename from tests/ui/redundant_pattern_matching.rs rename to tests/ui/redundant_pattern_matching_result.rs index d76f9c288ff..5d175294232 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -3,7 +3,6 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow( - clippy::unit_arg, unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching_result.stderr similarity index 80% rename from tests/ui/redundant_pattern_matching.stderr rename to tests/ui/redundant_pattern_matching_result.stderr index aeb309f5ba1..d6a46babb77 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching_result.rs:15:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:18:12 + --> $DIR/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:20:12 + --> $DIR/redundant_pattern_matching_result.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:22:15 + --> $DIR/redundant_pattern_matching_result.rs:21:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:24:15 + --> $DIR/redundant_pattern_matching_result.rs:23:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:34:5 + --> $DIR/redundant_pattern_matching_result.rs:33:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:39:5 + --> $DIR/redundant_pattern_matching_result.rs:38:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:44:5 + --> $DIR/redundant_pattern_matching_result.rs:43:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 + --> $DIR/redundant_pattern_matching_result.rs:48:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:54:20 + --> $DIR/redundant_pattern_matching_result.rs:53:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:60:20 + --> $DIR/redundant_pattern_matching_result.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:62:19 + --> $DIR/redundant_pattern_matching_result.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:85:19 + --> $DIR/redundant_pattern_matching_result.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:86:16 + --> $DIR/redundant_pattern_matching_result.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:92:12 + --> $DIR/redundant_pattern_matching_result.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:15 + --> $DIR/redundant_pattern_matching_result.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:111:12 + --> $DIR/redundant_pattern_matching_result.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:113:12 + --> $DIR/redundant_pattern_matching_result.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:115:15 + --> $DIR/redundant_pattern_matching_result.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:117:15 + --> $DIR/redundant_pattern_matching_result.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:119:5 + --> $DIR/redundant_pattern_matching_result.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:124:5 + --> $DIR/redundant_pattern_matching_result.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, From 0502ac2a87458620ae83e5260fd13149a558e090 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 18 Nov 2020 08:33:25 +0900 Subject: [PATCH 0973/1110] Improve doc about `map_clone` --- clippy_lints/src/map_clone.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 3f34dd5112e..220240acb7a 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -14,8 +14,9 @@ use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; declare_clippy_lint! { - /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests - /// `iterator.cloned()` instead + /// **What it does:** Checks for usage of `map(|x| x.clone())` or + /// dereferencing closures for `Copy` types, on `Iterator` or `Option`, + /// and suggests `cloned()` or `copied()` instead /// /// **Why is this bad?** Readability, this can be written more concisely /// From 6494bd0bac72b621fc4db64d68c94c8199e97afe Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 18 Nov 2020 12:36:47 +0900 Subject: [PATCH 0974/1110] Revert "Add `rustfmt::skip` as a work around" This reverts commit 0e803417f997ba35c0045704dd347e64c2a1786c. Fixed by https://github.com/rust-lang/rustfmt/issues/4528. --- tests/ui/cast_ref_to_mut.rs | 4 ---- tests/ui/cast_ref_to_mut.stderr | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs index 0ede958d170..089e5cfabe4 100644 --- a/tests/ui/cast_ref_to_mut.rs +++ b/tests/ui/cast_ref_to_mut.rs @@ -2,10 +2,6 @@ #![allow(clippy::no_effect)] extern "C" { - #[rustfmt::skip] - // TODO: This `rustfmt::skip` is a work around of #6336 because - // the following comments are checked by rustfmt for some reason. - // // N.B., mutability can be easily incorrect in FFI calls -- as // in C, the default is mutable pointers. fn ffi(c: *mut u8); diff --git a/tests/ui/cast_ref_to_mut.stderr b/tests/ui/cast_ref_to_mut.stderr index d36aa0e00ee..aacd99437d9 100644 --- a/tests/ui/cast_ref_to_mut.stderr +++ b/tests/ui/cast_ref_to_mut.stderr @@ -1,5 +1,5 @@ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:22:9 + --> $DIR/cast_ref_to_mut.rs:18:9 | LL | (*(a as *const _ as *mut String)).push_str(" world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | (*(a as *const _ as *mut String)).push_str(" world"); = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:23:9 + --> $DIR/cast_ref_to_mut.rs:19:9 | LL | *(a as *const _ as *mut _) = String::from("Replaced"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:24:9 + --> $DIR/cast_ref_to_mut.rs:20:9 | LL | *(a as *const _ as *mut String) += " world"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 4bf58bd55d1681332f7b35fbc8680e0741353c4f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 19 Nov 2020 11:22:59 +0100 Subject: [PATCH 0975/1110] Add CHANGELOG for 1.48 --- CHANGELOG.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 816d25bcd93..a00554f33b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,117 @@ document. ## Unreleased / In Rust Nightly -[e636b88...master](https://github.com/rust-lang/rust-clippy/compare/e636b88...master) +[b20d4c1...master](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...master) + +## Rust 1.49 + +Current beta, release 2020-12-31 + +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1) + +### New Lints + +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911) +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029) +* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081) +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092) +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092) +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101) +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103) +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109) +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123) +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135) +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157) +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165) +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177) +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183) +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226) +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227) +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233) + +### Moves and Deprecations + +* Rename `single_char_push_str` to [`single_char_add_str`] + [#6037](https://github.com/rust-lang/rust-clippy/pull/6037) +* Rename `zero_width_space` to [`invisible_characters`] + [#6105](https://github.com/rust-lang/rust-clippy/pull/6105) +* Deprecate [`drop_bounds`] (uplifted) + [#6111](https://github.com/rust-lang/rust-clippy/pull/6111) +* Move [`string_lit_as_bytes`] to `nursery` + [#6117](https://github.com/rust-lang/rust-clippy/pull/6117) +* Move [`rc_buffer`] to `restriction` + [#6128](https://github.com/rust-lang/rust-clippy/pull/6128) + +### Enhancements + +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a + reliable suggestion) + [#5727](https://github.com/rust-lang/rust-clippy/pull/5727) +* [`single_char_add_str`]: Also lint on `String::insert_str` + [#6037](https://github.com/rust-lang/rust-clippy/pull/6037) +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}` + [#6105](https://github.com/rust-lang/rust-clippy/pull/6105) +* [`eq_op`]: Also lint on the `assert_*!` macro family + [#6167](https://github.com/rust-lang/rust-clippy/pull/6167) +* [`items_after_statements`]: Also lint in local macro expansions + [#6176](https://github.com/rust-lang/rust-clippy/pull/6176) +* [`unnecessary_cast`]: Also lint casts on integer and float literals + [#6187](https://github.com/rust-lang/rust-clippy/pull/6187) +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or` + [#6190](https://github.com/rust-lang/rust-clippy/pull/6190) +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms + [#6216](https://github.com/rust-lang/rust-clippy/pull/6216) +* [`integer_arithmetic`]: Better handle `/` an `%` operators + [#6229](https://github.com/rust-lang/rust-clippy/pull/6229) + +### False Positive Fixes + +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the + lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978) +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it + is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076) +* [`or_fun_call`]: Revert changes addressing the handling of `const fn` + [#6077](https://github.com/rust-lang/rust-clippy/pull/6077) +* [`needless_range_loop`]: No longer lints, when the iterable is used in the + range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102) +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent + [#6104](https://github.com/rust-lang/rust-clippy/pull/6104) +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a + float (e.g. `713.32_64`) + [#6114](https://github.com/rust-lang/rust-clippy/pull/6114) +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex` + [#6132](https://github.com/rust-lang/rust-clippy/pull/6132) +* [`boxed_local`]: No longer lints on `extern fn` arguments + [#6133](https://github.com/rust-lang/rust-clippy/pull/6133) +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where` + clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198) + +### Suggestion Fixes/Improvements + +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter + [#6078](https://github.com/rust-lang/rust-clippy/pull/6078) +* [`needless_arbitrary_self_type`]: Correctly handle expanded code + [#6093](https://github.com/rust-lang/rust-clippy/pull/6093) +* [`useless_format`]: Preserve raw strings in suggestion + [#6151](https://github.com/rust-lang/rust-clippy/pull/6151) +* [`empty_loop`]: Suggest alternatives + [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) +* [`borrowed_box`]: Correctly add parentheses in suggestion + [#6200](https://github.com/rust-lang/rust-clippy/pull/6200) +* [`unused_unit`]: Improve suggestion formatting + [#6247](https://github.com/rust-lang/rust-clippy/pull/6247) + +### Documentation Improvements + +* Some doc improvements: + * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) + * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) +* [`doc_markdown`]: Document problematic link text style + [#6107](https://github.com/rust-lang/rust-clippy/pull/6107) ## Rust 1.48 -Current beta, release 2020-11-19 +Current stable, released 2020-11-19 [09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88) @@ -128,7 +234,7 @@ Current beta, release 2020-11-19 ## Rust 1.47 -Current stable, released 2020-10-08 +Released 2020-10-08 [c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) From a98acdba3e1e1697bdde26584972893edcdc49d1 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 19 Nov 2020 11:23:28 +0100 Subject: [PATCH 0976/1110] Fix trailing whitespaces in CHANGELOG file --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a00554f33b5..033d633438e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -162,7 +162,7 @@ Current stable, released 2020-11-19 * [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`] [#5994](https://github.com/rust-lang/rust-clippy/pull/5994) -* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts [#5999](https://github.com/rust-lang/rust-clippy/pull/5999) * [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures [#5920](https://github.com/rust-lang/rust-clippy/pull/5920) @@ -170,7 +170,7 @@ Current stable, released 2020-11-19 [#5949](https://github.com/rust-lang/rust-clippy/pull/5949) * [`doc_markdown`]: allow using "GraphQL" without backticks [#5996](https://github.com/rust-lang/rust-clippy/pull/5996) -* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self` +* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self` [#5971](https://github.com/rust-lang/rust-clippy/pull/5971) * [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays [#6034](https://github.com/rust-lang/rust-clippy/pull/6034) @@ -191,19 +191,19 @@ Current stable, released 2020-11-19 [#5946](https://github.com/rust-lang/rust-clippy/pull/5946) * [`useless_conversion`]: show the type in the error message [#6035](https://github.com/rust-lang/rust-clippy/pull/6035) -* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message [#5892](https://github.com/rust-lang/rust-clippy/pull/5892) * [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous [#6043](https://github.com/rust-lang/rust-clippy/pull/6043) * [`default_trait_access`]: do not use unnecessary type parameters in the suggestion [#5993](https://github.com/rust-lang/rust-clippy/pull/5993) -* [`collapsible_if`]: don't use expanded code in the suggestion +* [`collapsible_if`]: don't use expanded code in the suggestion [#5992](https://github.com/rust-lang/rust-clippy/pull/5992) * Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`] [#6042](https://github.com/rust-lang/rust-clippy/pull/6042) * [`unit_arg`]: improve the readability of the suggestion [#5931](https://github.com/rust-lang/rust-clippy/pull/5931) -* [`stable_sort_primitive`]: print the type that is being sorted in the lint message +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message [#5935](https://github.com/rust-lang/rust-clippy/pull/5935) * Show line count and max lines in [`too_many_lines`] lint message [#6009](https://github.com/rust-lang/rust-clippy/pull/6009) @@ -211,7 +211,7 @@ Current stable, released 2020-11-19 [#5900](https://github.com/rust-lang/rust-clippy/pull/5900) * [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly [#6024](https://github.com/rust-lang/rust-clippy/pull/6024) -* [`redundant_allocation`]: suggest replacing `Rc>` with `Rc` +* [`redundant_allocation`]: suggest replacing `Rc>` with `Rc` [#5899](https://github.com/rust-lang/rust-clippy/pull/5899) * Make lint messages adhere to rustc dev guide conventions [#5893](https://github.com/rust-lang/rust-clippy/pull/5893) From e9afdf074090d24f18ecc9d7bb3b20861615794a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 19 Nov 2020 11:23:59 +0100 Subject: [PATCH 0977/1110] Improve changlog update documentation --- doc/changelog_update.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/changelog_update.md b/doc/changelog_update.md index 49726464957..115848c4804 100644 --- a/doc/changelog_update.md +++ b/doc/changelog_update.md @@ -29,8 +29,11 @@ bullet points might be helpful: * When writing the release notes for the **upcoming beta release**, you need to check out the Clippy commit of the current Rust `master`. [Link][rust_master_tools] * When writing the (forgotten) release notes for a **past stable release**, you - need to select the Rust release tag from the dropdown and then check the - commit of the Clippy directory: + need to check out the Rust release tag of the stable release. + [Link][rust_stable_tools] + +Usually you want to wirte the changelog of the **upcoming stable release**. Make +sure though, that `beta` was already branched in the Rust repository. To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: ``` @@ -71,6 +74,19 @@ The order should roughly be: 7. Documentation improvements 8. Others +As section headers, we use: + +``` +### New Lints +### Moves and Deprecations +### Enhancements +### False Positive Fixes +### Suggestion Fixes/Improvements +### ICE Fixes +### Documentation Improvements +### Others +``` + Please also be sure to update the Beta/Unreleased sections at the top with the relevant commit ranges. @@ -78,3 +94,4 @@ relevant commit ranges. [forge]: https://forge.rust-lang.org/ [rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy +[rust_stable_tools]: https://github.com/rust-lang/rust/releases From dd4e471b3fb701312c2a3fabfba0011f239d4760 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 20 Nov 2020 09:37:47 +0100 Subject: [PATCH 0978/1110] Properly deprecate panic_params lint --- clippy_lints/src/deprecated_lints.rs | 5 +++++ clippy_lints/src/lib.rs | 4 ++++ src/lintlist/mod.rs | 7 ------- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +++++++- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 461c6e31d3e..1c3285ed701 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -181,3 +181,8 @@ declare_deprecated_lint! { pub TEMPORARY_CSTRING_AS_PTR, "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" } + +declare_deprecated_lint! { + pub PANIC_PARAMS, + "this lint has been uplifted to rustc and is now called `panic_fmt`" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19bf67d80c4..ff5d4846995 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -495,6 +495,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::temporary_cstring_as_ptr", "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", ); + store.register_removed( + "clippy::panic_params", + "this lint has been uplifted to rustc and is now called `panic_fmt`", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 702f9d86de6..213e5758659 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1831,13 +1831,6 @@ vec![ deprecation: None, module: "panic_in_result_fn", }, - Lint { - name: "panic_params", - group: "style", - desc: "missing parameters in `panic!` calls", - deprecation: None, - module: "panic_unimplemented", - }, Lint { name: "panicking_unwrap", group: "correctness", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 56755596c97..4cbc5630d75 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -10,5 +10,6 @@ #[warn(clippy::regex_macro)] #[warn(clippy::drop_bounds)] #[warn(clippy::temporary_cstring_as_ptr)] +#[warn(clippy::panic_params)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 37b726fc00f..a348d01d734 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -72,11 +72,17 @@ error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has LL | #[warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt`` + --> $DIR/deprecated.rs:13:8 + | +LL | #[warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors From 5ee0a400fc5104a0fb9be5c85c48d79cf1af8dce Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 20 Nov 2020 10:06:26 +0100 Subject: [PATCH 0979/1110] Fix dogfood errors --- clippy_lints/src/panic_unimplemented.rs | 2 +- clippy_lints/src/utils/ast_utils.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 8b10d071647..31b03ecd101 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -73,7 +73,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(_) = match_panic_call(cx, expr) { + if match_panic_call(cx, expr).is_some() { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { span_lint( diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 9050b9b2d9a..fcf7a4b1367 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -110,8 +110,7 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { match (l, r) { (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), - (StructRest::Rest(_), StructRest::Rest(_)) => true, - (StructRest::None, StructRest::None) => true, + (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, _ => false, } } From 8b21241fd57376ab35e8ae75b3f57b7c601cdcdb Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Fri, 20 Nov 2020 11:35:15 -0500 Subject: [PATCH 0980/1110] Revert "Convert the await holding lints to correctness" This reverts commit d8c6bce4407b1c99ed961f75a093ffe767818069. --- clippy_lints/src/await_holding_invalid.rs | 4 ++-- clippy_lints/src/lib.rs | 6 ++---- src/lintlist/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index ca819663fde..58892024ce2 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_LOCK, - correctness, + pedantic, "Inside an async function, holding a MutexGuard while calling await" } @@ -84,7 +84,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_REFCELL_REF, - correctness, + pedantic, "Inside an async function, holding a RefCell ref while calling await" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 36016988b6b..7e8cbd00c22 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1224,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), @@ -1327,8 +1329,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), @@ -1793,8 +1793,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&booleans::LOGIC_BUG), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c5a31fac254..79b24b83680 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -62,14 +62,14 @@ vec![ }, Lint { name: "await_holding_lock", - group: "correctness", + group: "pedantic", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", - group: "correctness", + group: "pedantic", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, module: "await_holding_invalid", From 9b910e19ce9bcd27c6dad220aba38121dc85138e Mon Sep 17 00:00:00 2001 From: oliver <16816606+o752d@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:44:47 +0000 Subject: [PATCH 0981/1110] a typo typo --- clippy_lints/src/assertions_on_constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a52f0997d43..62c73dbac48 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -129,7 +129,7 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) if let ExprKind::Block(ref block, _) = arms[0].body.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; - // inner block is optional. unwarp it if it exists, or use the expression as is otherwise. + // inner block is optional. unwrap it if it exists, or use the expression as is otherwise. if let Some(begin_panic_call) = match block_expr.kind { ExprKind::Block(ref inner_block, _) => &inner_block.expr, _ => &block.expr, From e30bb7661d5cb547c59deb7ff700d53ddc219a11 Mon Sep 17 00:00:00 2001 From: oliver Date: Sun, 22 Nov 2020 00:50:09 -0400 Subject: [PATCH 0982/1110] update --- tests/ui/manual_ok_or.fixed | 2 +- tests/ui/manual_ok_or.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed index b42e94bd727..887a97d7a01 100644 --- a/tests/ui/manual_ok_or.fixed +++ b/tests/ui/manual_ok_or.fixed @@ -28,7 +28,7 @@ fn main() { // not applicable, or side isn't `Result::Err` foo.map_or(Ok::(1), |v| Ok(v)); - // not applicatble, expr is not a `Result` value + // not applicable, expr is not a `Result` value foo.map_or(42, |v| v); // TODO patterns not covered yet diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs index e5a6056fbf5..3c99872f502 100644 --- a/tests/ui/manual_ok_or.rs +++ b/tests/ui/manual_ok_or.rs @@ -32,7 +32,7 @@ fn main() { // not applicable, or side isn't `Result::Err` foo.map_or(Ok::(1), |v| Ok(v)); - // not applicatble, expr is not a `Result` value + // not applicable, expr is not a `Result` value foo.map_or(42, |v| v); // TODO patterns not covered yet From 034244f1081ee65ce9a258e99cf39baa5d692bca Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sat, 21 Nov 2020 15:00:03 -0500 Subject: [PATCH 0983/1110] Small grammar, punctuation, and code style improvements to docs --- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/regex.rs | 4 ++-- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/useless_conversion.rs | 6 +++--- src/lintlist/mod.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8e2f03d6e4e..8842901d90b 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -69,7 +69,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for comparing to an empty slice such as "" or [],` + /// **What it does:** Checks for comparing to an empty slice such as `""` or `[]`, /// and suggests using `.is_empty()` where applicable. /// /// **Why is this bad?** Some structures can answer `.is_empty()` much faster diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ff0a3532465..fa174404365 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1351,7 +1351,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).collect::()`. + /// **What it does:** Checks for usage of `_.map(_).collect::()`. /// /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic. /// diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 95594e38c9e..d06ab143482 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -11,7 +11,7 @@ use std::convert::TryFrom; declare_clippy_lint! { /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation - /// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct + /// (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct /// regex syntax. /// /// **Why is this bad?** This will lead to a runtime panic. @@ -29,7 +29,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) - /// creation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`). + /// creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`). /// /// **Why is this bad?** Matching the regex can likely be replaced by `==` or /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str` diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index ade5fff5ffc..2501635e7ef 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// let mut twins = vec!((1,1), (2,2)); + /// let mut twins = vec!((1, 1), (2, 2)); /// twins.sort_by_key(|x| { x.1; }); /// ``` pub UNIT_RETURN_EXPECTING_ORD, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index c6194b0c6de..99c38e8f176 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -12,8 +12,8 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { - /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls - /// that useless converts to the same type as caller. + /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls + /// that uselessly convert to the same type. /// /// **Why is this bad?** Redundant code. /// @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` that perform useless conversions to the same type" } #[derive(Default)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 79b24b83680..1d906d20ad4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2751,7 +2751,7 @@ vec![ Lint { name: "useless_conversion", group: "complexity", - desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type", + desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type", deprecation: None, module: "useless_conversion", }, From 445466e567c192bb81ccc366743793ffb079da5d Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" <193874+carols10cents@users.noreply.github.com> Date: Sun, 22 Nov 2020 10:12:41 -0500 Subject: [PATCH 0984/1110] Apply suggestions from code review to change "that" to "which" Co-authored-by: oliver <16816606+o752d@users.noreply.github.com> --- clippy_lints/src/useless_conversion.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 99c38e8f176..efa9c3fab4a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -13,7 +13,7 @@ use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls - /// that uselessly convert to the same type. + /// which uselessly convert to the same type. /// /// **Why is this bad?** Redundant code. /// @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` that perform useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type" } #[derive(Default)] From a8c47440b4ac59239f11838cfc7426b2adef3685 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 10 Nov 2020 09:19:30 -0600 Subject: [PATCH 0985/1110] Use article_and_description for missing docs --- clippy_lints/src/missing_doc.rs | 64 ++++++++++++----------- tests/ui/missing-doc-crate-missing.stderr | 2 +- tests/ui/missing-doc-impl.stderr | 12 ++--- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 009e3d8937e..f2c0ab1222c 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -2,7 +2,7 @@ // *rustc*'s // [`missing_doc`]. // -// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246 +// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // use crate::utils::span_lint; @@ -70,7 +70,14 @@ impl MissingDoc { } } - fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { + fn check_missing_docs_attrs( + &self, + cx: &LateContext<'_>, + attrs: &[ast::Attribute], + sp: Span, + article: &'static str, + desc: &'static str, + ) { // If we're building a test harness, then warning about // documentation is probably not really relevant right now. if cx.sess().opts.test { @@ -94,7 +101,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {}", desc), + &format!("missing documentation for {} {}", article, desc), ); } } @@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate"); + self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - let desc = match it.kind { - hir::ItemKind::Const(..) => "a constant", - hir::ItemKind::Enum(..) => "an enum", + match it.kind { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { @@ -136,16 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { return; } } - "a function" }, - hir::ItemKind::Mod(..) => "a module", - hir::ItemKind::Static(..) => "a static", - hir::ItemKind::Struct(..) => "a struct", - hir::ItemKind::Trait(..) => "a trait", - hir::ItemKind::TraitAlias(..) => "a trait alias", - hir::ItemKind::TyAlias(..) => "a type alias", - hir::ItemKind::Union(..) => "a union", - hir::ItemKind::OpaqueTy(..) => "an existential type", + hir::ItemKind::Const(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::OpaqueTy(..) => {}, hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod(..) | hir::ItemKind::GlobalAsm(..) @@ -153,17 +159,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Use(..) => return, }; - self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc); + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { - let desc = match trait_item.kind { - hir::TraitItemKind::Const(..) => "an associated constant", - hir::TraitItemKind::Fn(..) => "a trait method", - hir::TraitItemKind::Type(..) => "an associated type", - }; + let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc); + self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { @@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { }, } - let desc = match impl_item.kind { - hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::TyAlias(_) => "an associated type", - }; - self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc); } fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field"); + self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field"); } } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant"); + self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant"); } } diff --git a/tests/ui/missing-doc-crate-missing.stderr b/tests/ui/missing-doc-crate-missing.stderr index da46f988636..d56c5cc4c3a 100644 --- a/tests/ui/missing-doc-crate-missing.stderr +++ b/tests/ui/missing-doc-crate-missing.stderr @@ -1,4 +1,4 @@ -error: missing documentation for crate +error: missing documentation for the crate --> $DIR/missing-doc-crate-missing.rs:1:1 | LL | / #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing-doc-impl.stderr b/tests/ui/missing-doc-impl.stderr index 9656a39abce..7e10404ca00 100644 --- a/tests/ui/missing-doc-impl.stderr +++ b/tests/ui/missing-doc-impl.stderr @@ -51,13 +51,13 @@ LL | | fn foo_with_impl(&self) {} LL | | } | |_^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:39:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:40:5 | LL | fn foo_with_impl(&self) {} @@ -75,25 +75,25 @@ error: missing documentation for an associated type LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:62:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:63:5 | LL | fn bar() {} | ^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:67:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:70:5 | LL | fn foo2() {} From a39a93faeb4969fac62e896138cdf72377fa5502 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 22 Nov 2020 19:21:01 -0600 Subject: [PATCH 0986/1110] Disable unnecessary_cast for cfg-dependant types --- clippy_lints/src/types.rs | 10 +++++++++- tests/ui/unnecessary_cast.rs | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f0e10e374e1..840adbbc57a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -8,6 +8,7 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -1632,7 +1633,14 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if expr.span.from_expansion() { return; } - if let ExprKind::Cast(ref ex, _) = expr.kind { + if let ExprKind::Cast(ref ex, cast_to) = expr.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { + if let Res::Def(_, def_id) = path.res { + if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { + return; + } + } + } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); if let Some(lit) = get_numeric_literal(ex) { diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index df9b227eeb3..e8f2fb46665 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -20,4 +20,7 @@ fn main() { foo!(a, i32); foo!(b, f32); foo!(c, f64); + + // do not lint cast to cfg-dependant type + 1 as std::os::raw::c_char; } From dc075b426675e61cc46089046cf41d85edd79aaa Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 25 Nov 2020 02:01:05 +0100 Subject: [PATCH 0987/1110] Change `redundant_pattern_matching` to also lint `std::net::IpAddr` Suggest using utility methods `is_ipv4` and `is_ipv6`. --- clippy_lints/src/matches.rs | 25 +++- clippy_lints/src/utils/paths.rs | 2 + .../redundant_pattern_matching_ipaddr.fixed | 73 ++++++++++ tests/ui/redundant_pattern_matching_ipaddr.rs | 91 ++++++++++++ .../redundant_pattern_matching_ipaddr.stderr | 130 ++++++++++++++++++ .../redundant_pattern_matching_option.fixed | 5 +- tests/ui/redundant_pattern_matching_option.rs | 5 +- .../redundant_pattern_matching_option.stderr | 18 +-- .../ui/redundant_pattern_matching_poll.fixed | 5 +- tests/ui/redundant_pattern_matching_poll.rs | 5 +- .../ui/redundant_pattern_matching_poll.stderr | 18 +-- 11 files changed, 341 insertions(+), 36 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.fixed create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.rs create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.stderr diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e801..a14b63faf78 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -411,8 +411,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or - /// `std::task::Poll` + /// **What it does:** Lint for redundant pattern matching over `Result`, `Option`, + /// `std::task::Poll` or `std::net::IpAddr` /// /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function @@ -423,12 +423,15 @@ declare_clippy_lint! { /// /// ```rust /// # use std::task::Poll; + /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// if let Ok(_) = Ok::(42) {} /// if let Err(_) = Err::(42) {} /// if let None = None::<()> {} /// if let Some(_) = Some(42) {} /// if let Poll::Pending = Poll::Pending::<()> {} /// if let Poll::Ready(_) = Poll::Ready(42) {} + /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {} + /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {} /// match Ok::(42) { /// Ok(_) => true, /// Err(_) => false, @@ -439,12 +442,15 @@ declare_clippy_lint! { /// /// ```rust /// # use std::task::Poll; + /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// if Ok::(42).is_ok() {} /// if Err::(42).is_err() {} /// if None::<()>.is_none() {} /// if Some(42).is_some() {} /// if Poll::Pending::<()>.is_pending() {} /// if Poll::Ready(42).is_ready() {} + /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {} /// Ok::(42).is_ok(); /// ``` pub REDUNDANT_PATTERN_MATCHING, @@ -1546,6 +1552,10 @@ mod redundant_pattern_match { "is_some()" } else if match_qpath(path, &paths::POLL_READY) { "is_ready()" + } else if match_qpath(path, &paths::IPADDR_V4) { + "is_ipv4()" + } else if match_qpath(path, &paths::IPADDR_V6) { + "is_ipv6()" } else { return; } @@ -1626,6 +1636,17 @@ mod redundant_pattern_match { "is_ok()", "is_err()", ) + .or_else(|| { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::IPADDR_V4, + &paths::IPADDR_V6, + "is_ipv4()", + "is_ipv6()", + ) + }) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 829e9a2989c..61aeabb7ba7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -58,6 +58,8 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; +pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; +pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed new file mode 100644 index 00000000000..acc8de5f41e --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::net::{ + IpAddr::{self, V4, V6}, + Ipv4Addr, Ipv6Addr, +}; + +fn main() { + let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); + if ipaddr.is_ipv4() {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) { + println!("{}", ipaddr); + } + + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + + V4(Ipv4Addr::LOCALHOST).is_ipv6(); + + V6(Ipv6Addr::LOCALHOST).is_ipv6(); + + V6(Ipv6Addr::LOCALHOST).is_ipv4(); + + let _ = if V4(Ipv4Addr::LOCALHOST).is_ipv4() { + true + } else { + false + }; + + ipaddr_const(); + + let _ = if gen_ipaddr().is_ipv4() { + 1 + } else if gen_ipaddr().is_ipv6() { + 2 + } else { + 3 + }; +} + +fn gen_ipaddr() -> IpAddr { + V4(Ipv4Addr::LOCALHOST) +} + +const fn ipaddr_const() { + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + + V6(Ipv6Addr::LOCALHOST).is_ipv6(); +} diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs new file mode 100644 index 00000000000..678d91ce93a --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -0,0 +1,91 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::net::{ + IpAddr::{self, V4, V6}, + Ipv4Addr, Ipv6Addr, +}; + +fn main() { + let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); + if let V4(_) = &ipaddr {} + + if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) { + println!("{}", ipaddr); + } + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { + true + } else { + false + }; + + ipaddr_const(); + + let _ = if let V4(_) = gen_ipaddr() { + 1 + } else if let V6(_) = gen_ipaddr() { + 2 + } else { + 3 + }; +} + +fn gen_ipaddr() -> IpAddr { + V4(Ipv4Addr::LOCALHOST) +} + +const fn ipaddr_const() { + if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr new file mode 100644 index 00000000000..caf458cd862 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -0,0 +1,130 @@ +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12 + | +LL | if let V4(_) = &ipaddr {} + | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + | +LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + | +LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20 + | +LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20 + | +LL | let _ = if let V4(_) = gen_ipaddr() { + | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19 + | +LL | } else if let V6(_) = gen_ipaddr() { + | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 + | +LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 + | +LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index bc369dd2491..66f580a0a68 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -37,8 +37,7 @@ fn main() { let _ = None::<()>.is_none(); let opt = Some(false); - let x = if opt.is_some() { true } else { false }; - takes_bool(x); + let _ = if opt.is_some() { true } else { false }; issue6067(); @@ -55,8 +54,6 @@ fn gen_opt() -> Option<()> { None } -fn takes_bool(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index d7616a72913..f18b27b8b95 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -46,8 +46,7 @@ fn main() { }; let opt = Some(false); - let x = if let Some(_) = opt { true } else { false }; - takes_bool(x); + let _ = if let Some(_) = opt { true } else { false }; issue6067(); @@ -64,8 +63,6 @@ fn gen_opt() -> Option<()> { None } -fn takes_bool(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 7ddfbe503a2..58482a0ab70 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -73,47 +73,47 @@ LL | | }; error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:49:20 | -LL | let x = if let Some(_) = opt { true } else { false }; +LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:54:20 + --> $DIR/redundant_pattern_matching_option.rs:53:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:56:19 + --> $DIR/redundant_pattern_matching_option.rs:55:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:77:12 + --> $DIR/redundant_pattern_matching_option.rs:74:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:79:12 + --> $DIR/redundant_pattern_matching_option.rs:76:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:81:15 + --> $DIR/redundant_pattern_matching_option.rs:78:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:83:15 + --> $DIR/redundant_pattern_matching_option.rs:80:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:85:5 + --> $DIR/redundant_pattern_matching_option.rs:82:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:90:5 + --> $DIR/redundant_pattern_matching_option.rs:87:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed index 564c427f063..465aa80dac2 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -34,8 +34,7 @@ fn main() { let _ = Pending::<()>.is_pending(); let poll = Ready(false); - let x = if poll.is_ready() { true } else { false }; - takes_poll(x); + let _ = if poll.is_ready() { true } else { false }; poll_const(); @@ -52,8 +51,6 @@ fn gen_poll() -> Poll<()> { Pending } -fn takes_poll(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index d453d4184af..7891ff353b1 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -43,8 +43,7 @@ fn main() { }; let poll = Ready(false); - let x = if let Ready(_) = poll { true } else { false }; - takes_poll(x); + let _ = if let Ready(_) = poll { true } else { false }; poll_const(); @@ -61,8 +60,6 @@ fn gen_poll() -> Poll<()> { Pending } -fn takes_poll(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 42e5d6f41fe..5ffc6c47c90 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -67,47 +67,47 @@ LL | | }; error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:46:20 | -LL | let x = if let Ready(_) = poll { true } else { false }; +LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try this: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:51:20 + --> $DIR/redundant_pattern_matching_poll.rs:50:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:53:19 + --> $DIR/redundant_pattern_matching_poll.rs:52:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:71:12 + --> $DIR/redundant_pattern_matching_poll.rs:68:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:73:12 + --> $DIR/redundant_pattern_matching_poll.rs:70:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:75:15 + --> $DIR/redundant_pattern_matching_poll.rs:72:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:77:15 + --> $DIR/redundant_pattern_matching_poll.rs:74:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:79:5 + --> $DIR/redundant_pattern_matching_poll.rs:76:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -116,7 +116,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:84:5 + --> $DIR/redundant_pattern_matching_poll.rs:81:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, From aaa43250455c19ae54c821518c42219f6460038c Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 21 Oct 2020 18:17:34 +0530 Subject: [PATCH 0988/1110] add support for minimum supported rust version. add configuration option for minimum supported rust version add msrv attribute to some lints listed in #6097 add tests --- clippy_lints/src/lib.rs | 23 +++++-- clippy_lints/src/manual_non_exhaustive.rs | 33 ++++++++- clippy_lints/src/manual_strip.rs | 37 ++++++++-- clippy_lints/src/matches.rs | 39 +++++++++-- clippy_lints/src/methods/mod.rs | 51 +++++++++++--- clippy_lints/src/utils/attrs.rs | 17 +++++ clippy_lints/src/utils/conf.rs | 2 + clippy_lints/src/utils/mod.rs | 49 +++++++++++++ .../invalid_min_rust_version/clippy.toml | 1 + .../invalid_min_rust_version.rs | 3 + .../invalid_min_rust_version.stderr | 4 ++ tests/ui-toml/min_rust_version/clippy.toml | 1 + .../min_rust_version/min_rust_version.rs | 68 +++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/min_rust_version_attr.rs | 51 ++++++++++++++ tests/ui/min_rust_version_invalid_attr.rs | 4 ++ tests/ui/min_rust_version_invalid_attr.stderr | 8 +++ tests/ui/min_rust_version_no_patch.rs | 14 ++++ tests/ui/min_rust_version_outer_attr.rs | 4 ++ tests/ui/min_rust_version_outer_attr.stderr | 8 +++ 20 files changed, 390 insertions(+), 29 deletions(-) create mode 100644 tests/ui-toml/invalid_min_rust_version/clippy.toml create mode 100644 tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs create mode 100644 tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr create mode 100644 tests/ui-toml/min_rust_version/clippy.toml create mode 100644 tests/ui-toml/min_rust_version/min_rust_version.rs create mode 100644 tests/ui/min_rust_version_attr.rs create mode 100644 tests/ui/min_rust_version_invalid_attr.rs create mode 100644 tests/ui/min_rust_version_invalid_attr.stderr create mode 100644 tests/ui/min_rust_version_no_patch.rs create mode 100644 tests/ui/min_rust_version_outer_attr.rs create mode 100644 tests/ui/min_rust_version_outer_attr.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e8cbd00c22..866dae110cc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -44,6 +44,7 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate rustc_typeck; +use crate::utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; @@ -933,7 +934,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -969,7 +969,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box methods::Methods); + + let parsed_msrv = conf.msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); + None + }) + }); + + let msrv = parsed_msrv.clone(); + store.register_late_pass(move || box methods::Methods::new(msrv.clone())); + let msrv = parsed_msrv.clone(); + store.register_late_pass(move || box matches::Matches::new(msrv.clone())); + let msrv = parsed_msrv.clone(); + store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone())); + let msrv = parsed_msrv; + store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone())); + store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -983,7 +999,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); - store.register_late_pass(|| box matches::Matches::default()); store.register_late_pass(|| box minmax::MinMaxPass); store.register_late_pass(|| box open_options::OpenOptions); store.register_late_pass(|| box zero_div_zero::ZeroDiv); @@ -1144,7 +1159,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); - store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); @@ -1166,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index a1450b0d5fe..4762ba16ac7 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,11 +1,20 @@ -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::{get_inner_attr, meets_msrv, 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_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use semver::{Version, VersionReq}; + +const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version { + major: 1, + minor: 40, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -55,10 +64,26 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +#[derive(Clone)] +pub struct ManualNonExhaustive { + msrv: Option, +} + +impl ManualNonExhaustive { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustive { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) { + return; + } + match &item.kind { ItemKind::Enum(def, _) => { check_manual_non_exhaustive_enum(cx, item, &def.variants); @@ -73,6 +98,8 @@ impl EarlyLintPass for ManualNonExhaustive { _ => {}, } } + + extract_msrv_attr!(EarlyContext); } fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 4afb0ab3bad..446641ca114 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,21 +1,31 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, + eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; +use semver::{Version, VersionReq}; + +const MANUAL_STRIP_MSRV: Version = Version { + major: 1, + minor: 45, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; declare_clippy_lint! { /// **What it does:** @@ -51,7 +61,18 @@ declare_clippy_lint! { "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing" } -declare_lint_pass!(ManualStrip => [MANUAL_STRIP]); +pub struct ManualStrip { + msrv: Option, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualStrip => [MANUAL_STRIP]); #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum StripKind { @@ -61,6 +82,10 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + return; + } + if_chain! { if let Some((cond, then, _)) = higher::if_block(&expr); if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; @@ -114,6 +139,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } } } + + extract_msrv_attr!(LateContext); } // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e801..5c38abbd9c6 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet, - snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, + is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, + remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; @@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::{sym, Symbol}; +use semver::{Version, VersionReq}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -527,9 +528,20 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { + msrv: Option, infallible_destructuring_match_linted: bool, } +impl Matches { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + ..Matches::default() + } + } +} + impl_lint_pass!(Matches => [ SINGLE_MATCH, MATCH_REF_PATS, @@ -549,6 +561,14 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); +const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version { + major: 1, + minor: 42, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; + impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { @@ -556,7 +576,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check(cx, expr); - if !check_match_like_matches(cx, expr) { + + if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) { + if !check_match_like_matches(cx, expr) { + lint_match_arms(cx, expr); + } + } else { lint_match_arms(cx, expr); } @@ -640,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } } + + extract_msrv_attr!(LateContext); } #[rustfmt::skip] diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa174404365..6b478986067 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -12,6 +12,7 @@ use std::iter; use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -20,7 +21,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -28,12 +29,14 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, - match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, - single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + walk_ptrs_ty_depth, SpanlessEq, }; +use semver::{Version, VersionReq}; declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. @@ -1404,7 +1407,18 @@ declare_clippy_lint! { "use `.collect()` instead of `::from_iter()`" } -declare_lint_pass!(Methods => [ +pub struct Methods { + msrv: Option, +} + +impl Methods { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, @@ -1531,8 +1545,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { check_pointer_offset(cx, expr, arg_lists[0]) }, ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), - ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), - ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["map", "as_ref"] => { + lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) + }, + ["map", "as_mut"] => { + lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) + }, ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), @@ -1738,6 +1756,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + extract_msrv_attr!(LateContext); } /// Checks for the `OR_FUN_CALL` lint. @@ -3453,6 +3473,14 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { ); } +const OPTION_AS_REF_DEREF_MSRV: Version = Version { + major: 1, + minor: 40, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; + /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s fn lint_option_as_ref_deref<'tcx>( cx: &LateContext<'tcx>, @@ -3460,7 +3488,12 @@ fn lint_option_as_ref_deref<'tcx>( as_ref_args: &[hir::Expr<'_>], map_args: &[hir::Expr<'_>], is_mut: bool, + msrv: Option<&VersionReq>, ) { + if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + return; + } + let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index e6d41341a55..aed6ee5dc57 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -21,6 +21,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ DeprecationStatus::Replaced("cognitive_complexity"), ), ("dump", DeprecationStatus::None), + ("msrv", DeprecationStatus::None), ]; pub struct LimitStack { @@ -123,6 +124,22 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } +pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { + let mut unique_attr = None; + for attr in get_attr(sess, attrs, name) { + match attr.style { + ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), + ast::AttrStyle::Inner => { + sess.span_err(attr.span, &format!("`{}` is defined multiple times", name)); + }, + ast::AttrStyle::Outer => { + sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); + }, + } + } + unique_attr +} + /// Return true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0ac8fff69f0..fc6304118d9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,6 +106,8 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { + /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports + (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e9c71e23a67..f8e88512048 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -51,6 +51,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym as rustc_sym; @@ -58,10 +59,58 @@ use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; +use semver::{Version, VersionReq}; use smallvec::SmallVec; use crate::consts::{constant, Constant}; +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = VersionReq::parse(msrv) { + return Some(version); + } else if let Some(sess) = sess { + if let Some(span) = span { + sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv)); + } + } + None +} + +pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { + msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) +} + +#[macro_export] +macro_rules! extract_msrv_attr { + (LateContext) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::LateContext<'tcx>, attrs: &'tcx [Attribute]) { + match get_inner_attr(cx.sess(), attrs, "msrv") { + Some(msrv_attr) => { + if let Some(msrv) = msrv_attr.value_str() { + self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess()), Some(msrv_attr.span)); + } else { + cx.sess().span_err(msrv_attr.span, "bad clippy attribute"); + } + }, + _ => (), + } + } + }; + (EarlyContext) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::EarlyContext<'tcx>, attrs: &'tcx [Attribute]) { + match get_inner_attr(cx.sess, attrs, "msrv") { + Some(msrv_attr) => { + if let Some(msrv) = msrv_attr.value_str() { + self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess), Some(msrv_attr.span)); + } else { + cx.sess.span_err(msrv_attr.span, "bad clippy attribute"); + } + }, + _ => (), + } + } + }; +} + /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). #[must_use] diff --git a/tests/ui-toml/invalid_min_rust_version/clippy.toml b/tests/ui-toml/invalid_min_rust_version/clippy.toml new file mode 100644 index 00000000000..088b12b2dac --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "invalid.version" diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs new file mode 100644 index 00000000000..2ebf28645e5 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs @@ -0,0 +1,3 @@ +#![allow(clippy::redundant_clone)] + +fn main() {} diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr new file mode 100644 index 00000000000..e9d8fd2e0f5 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version + +error: aborting due to previous error + diff --git a/tests/ui-toml/min_rust_version/clippy.toml b/tests/ui-toml/min_rust_version/clippy.toml new file mode 100644 index 00000000000..8e17d8074c4 --- /dev/null +++ b/tests/ui-toml/min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "1.0.0" diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs new file mode 100644 index 00000000000..bc41efa42a1 --- /dev/null +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -0,0 +1,68 @@ +#![allow(clippy::redundant_clone)] +#![warn(clippy::manual_non_exhaustive)] + +use std::ops::Deref; + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // user forgot to remove the marker + #[non_exhaustive] + enum Ep { + A, + B, + #[doc(hidden)] + _C, + } +} + +fn option_as_ref_deref() { + let mut opt = Some(String::from("123")); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); +} + +fn match_like_matches() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn match_same_arms() { + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; +} + +fn match_same_arms2() { + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; +} + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + option_as_ref_deref(); + match_like_matches(); + match_same_arms(); + match_same_arms2(); + manual_strip_msrv(); +} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index a58e7e918e2..af3d9ecf6e8 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -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`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs new file mode 100644 index 00000000000..8ed483a3ac6 --- /dev/null +++ b/tests/ui/min_rust_version_attr.rs @@ -0,0 +1,51 @@ +#![allow(clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.0.0"] + +use std::ops::Deref; + +fn option_as_ref_deref() { + let mut opt = Some(String::from("123")); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); +} + +fn match_like_matches() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn match_same_arms() { + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; +} + +fn match_same_arms2() { + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; +} + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + option_as_ref_deref(); + match_like_matches(); + match_same_arms(); + match_same_arms2(); + manual_strip_msrv(); +} diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs new file mode 100644 index 00000000000..f20841891a7 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] +#![clippy::msrv = "invalid.version"] + +fn main() {} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr new file mode 100644 index 00000000000..6ff88ca56f8 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -0,0 +1,8 @@ +error: `invalid.version` is not a valid Rust version + --> $DIR/min_rust_version_invalid_attr.rs:2:1 + | +LL | #![clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs new file mode 100644 index 00000000000..515fe8f95e9 --- /dev/null +++ b/tests/ui/min_rust_version_no_patch.rs @@ -0,0 +1,14 @@ +#![allow(clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "^1.0"] + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + manual_strip_msrv() +} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs new file mode 100644 index 00000000000..551948bd72e --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] + +#[clippy::msrv = "invalid.version"] +fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr new file mode 100644 index 00000000000..579ee7a87d2 --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.stderr @@ -0,0 +1,8 @@ +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_outer_attr.rs:3:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From b2e2c0806ec2296bf7d98e0db5cc9c39a55f23ac Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 25 Nov 2020 11:50:13 +0100 Subject: [PATCH 0989/1110] Improve extract_msrv_attr! situation --- clippy_lints/src/manual_non_exhaustive.rs | 2 +- clippy_lints/src/manual_strip.rs | 5 ++-- clippy_lints/src/matches.rs | 10 ++++---- clippy_lints/src/methods/mod.rs | 12 ++++----- clippy_lints/src/utils/mod.rs | 30 ++++++++++------------- 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 4762ba16ac7..703e6feeca5 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_inner_attr, meets_msrv, snippet_opt, span_lint_and_then}; +use crate::utils::{meets_msrv, 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; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 446641ca114..e17e3adb94f 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,12 +1,11 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, - span_lint_and_then, + eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, LitKind}; +use rustc_ast::ast::LitKind; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::BinOpKind; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 5c38abbd9c6..d695af4de21 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, - is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, - remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, - span_lint_and_sugg, span_lint_and_then, + expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, + is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, + snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, LitKind}; +use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6b478986067..50dd760432d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -12,7 +12,6 @@ use std::iter; use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; -use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -29,12 +28,11 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher, - implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, - method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, + is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, + match_trait_method, match_type, match_var, meets_msrv, method_calls, method_chain_args, paths, remove_blocks, + return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; use semver::{Version, VersionReq}; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index f8e88512048..6f89e51279a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -79,30 +79,26 @@ pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) } -#[macro_export] macro_rules! extract_msrv_attr { (LateContext) => { - fn enter_lint_attrs(&mut self, cx: &rustc_lint::LateContext<'tcx>, attrs: &'tcx [Attribute]) { - match get_inner_attr(cx.sess(), attrs, "msrv") { - Some(msrv_attr) => { - if let Some(msrv) = msrv_attr.value_str() { - self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess()), Some(msrv_attr.span)); - } else { - cx.sess().span_err(msrv_attr.span, "bad clippy attribute"); - } - }, - _ => (), - } - } + extract_msrv_attr!(@LateContext, ()); }; (EarlyContext) => { - fn enter_lint_attrs(&mut self, cx: &rustc_lint::EarlyContext<'tcx>, attrs: &'tcx [Attribute]) { - match get_inner_attr(cx.sess, attrs, "msrv") { + extract_msrv_attr!(@EarlyContext); + }; + (@$context:ident$(, $call:tt)?) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) { + use $crate::utils::get_unique_inner_attr; + match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") { Some(msrv_attr) => { if let Some(msrv) = msrv_attr.value_str() { - self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess), Some(msrv_attr.span)); + self.msrv = $crate::utils::parse_msrv( + &msrv.to_string(), + Some(cx.sess$($call)?), + Some(msrv_attr.span), + ); } else { - cx.sess.span_err(msrv_attr.span, "bad clippy attribute"); + cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute"); } }, _ => (), From 93f922a85843b2c04e47ea30c109f0bd305e039e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 25 Nov 2020 12:19:13 +0100 Subject: [PATCH 0990/1110] Add note where the first definition of msrv attr is --- clippy_lints/src/utils/attrs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index aed6ee5dc57..24052a243af 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -130,7 +130,9 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s match attr.style { ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), ast::AttrStyle::Inner => { - sess.span_err(attr.span, &format!("`{}` is defined multiple times", name)); + sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name)) + .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") + .emit(); }, ast::AttrStyle::Outer => { sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); From d06076c0c56a2b254b061d569d421b887a7c6bbe Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 25 Nov 2020 12:19:42 +0100 Subject: [PATCH 0991/1110] Add test for multiple defined msrv attrs --- .../min_rust_version_multiple_inner_attr.rs | 11 ++++++ ...in_rust_version_multiple_inner_attr.stderr | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs create mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs new file mode 100644 index 00000000000..e882d5ccf91 --- /dev/null +++ b/tests/ui/min_rust_version_multiple_inner_attr.rs @@ -0,0 +1,11 @@ +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.40"] +#![clippy::msrv = "=1.35.0"] +#![clippy::msrv = "1.10.1"] + +mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] +} + +fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr new file mode 100644 index 00000000000..e3ff6605cde --- /dev/null +++ b/tests/ui/min_rust_version_multiple_inner_attr.stderr @@ -0,0 +1,38 @@ +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + From 2345ef5b1f89d9cac7a6697de08c36bfb771fe12 Mon Sep 17 00:00:00 2001 From: PunitLodha Date: Sat, 14 Nov 2020 19:21:33 +0530 Subject: [PATCH 0992/1110] added lints str_to_string and string_to_string --- clippy_lints/src/deprecated_lints.rs | 20 ------ clippy_lints/src/lib.rs | 22 ++---- clippy_lints/src/strings.rs | 100 ++++++++++++++++++++++++++- src/lintlist/mod.rs | 14 ++++ tests/ui/deprecated.rs | 2 - tests/ui/deprecated.stderr | 46 +++++------- tests/ui/deprecated_old.rs | 2 - tests/ui/deprecated_old.stderr | 30 +++----- tests/ui/str_to_string.rs | 7 ++ tests/ui/str_to_string.stderr | 19 +++++ tests/ui/string_to_string.rs | 7 ++ tests/ui/string_to_string.stderr | 11 +++ 12 files changed, 189 insertions(+), 91 deletions(-) create mode 100644 tests/ui/str_to_string.rs create mode 100644 tests/ui/str_to_string.stderr create mode 100644 tests/ui/string_to_string.rs create mode 100644 tests/ui/string_to_string.stderr diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 1c3285ed701..bec0c9f93a0 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -51,26 +51,6 @@ declare_deprecated_lint! { "`Vec::as_mut_slice` has been stabilized in 1.7" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This used to check for `.to_string()` method calls on values - /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be - /// specialized to be as efficient as `to_owned`. - pub STR_TO_STRING, - "using `str::to_string` is common even today and specialization will likely happen soon" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This used to check for `.to_string()` method calls on values - /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be - /// specialized to be as efficient as `clone`. - pub STRING_TO_STRING, - "using `string::to_string` is common even today and specialization will likely happen soon" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 866dae110cc..67a3a3fcf48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -441,14 +441,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7", ); - store.register_removed( - "clippy::str_to_string", - "using `str::to_string` is common even today and specialization will likely happen soon", - ); - store.register_removed( - "clippy::string_to_string", - "using `string::to_string` is common even today and specialization will likely happen soon", - ); store.register_removed( "clippy::misaligned_transmute", "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", @@ -840,6 +832,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &strings::STRING_ADD_ASSIGN, &strings::STRING_FROM_UTF8_AS_BYTES, &strings::STRING_LIT_AS_BYTES, + &strings::STRING_TO_STRING, + &strings::STR_TO_STRING, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &swap::ALMOST_SWAPPED, @@ -1186,6 +1180,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); + store.register_late_pass(|| box strings::StrToString); + store.register_late_pass(|| box strings::StringToString); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1228,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&strings::STRING_TO_STRING), + LintId::of(&strings::STR_TO_STRING), LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), @@ -1943,14 +1941,6 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { "unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7", ); - store.register_removed( - "str_to_string", - "using `str::to_string` is common even today and specialization will likely happen soon", - ); - store.register_removed( - "string_to_string", - "using `string::to_string` is common even today and specialization will likely happen soon", - ); store.register_removed( "misaligned_transmute", "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ede37624f71..42c45be3b45 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -2,6 +2,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use rustc_span::sym; @@ -11,7 +12,7 @@ use if_chain::if_chain; use crate::utils::SpanlessEq; use crate::utils::{ get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, - span_lint_and_sugg, + span_lint_and_help, span_lint_and_sugg, }; declare_clippy_lint! { @@ -289,3 +290,100 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } } } + +declare_clippy_lint! { + /// **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`. + /// + /// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string. + /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better + /// expressed with `.to_owned()`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let _ = "str".to_string(); + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// let _ = "str".to_owned(); + /// ``` + pub STR_TO_STRING, + restriction, + "using `to_string()` on a `&str`, which should be `to_owned()`" +} + +declare_lint_pass!(StrToString => [STR_TO_STRING]); + +impl LateLintPass<'_> for StrToString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if path.ident.name == sym!(to_string); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, ty, ..) = ty.kind(); + if *ty.kind() == ty::Str; + then { + span_lint_and_help( + cx, + STR_TO_STRING, + expr.span, + "`to_string()` called on a `&str`", + None, + "consider using `.to_owned()`", + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** This lint checks for `.to_string()` method calls on values of type `String`. + /// + /// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string. + /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`. + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let msg = String::from("Hello World"); + /// let _ = msg.to_string(); + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// let msg = String::from("Hello World"); + /// let _ = msg.clone(); + /// ``` + pub STRING_TO_STRING, + restriction, + "using `to_string()` on a `String`, which should be `clone()`" +} + +declare_lint_pass!(StringToString => [STRING_TO_STRING]); + +impl LateLintPass<'_> for StringToString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if path.ident.name == sym!(to_string); + let ty = cx.typeck_results().expr_ty(&args[0]); + if is_type_diagnostic_item(cx, ty, sym!(string_type)); + then { + span_lint_and_help( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + None, + "consider using `.clone()`", + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1d906d20ad4..a104f687bdf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2237,6 +2237,13 @@ vec![ deprecation: None, module: "stable_sort_primitive", }, + Lint { + name: "str_to_string", + group: "restriction", + desc: "using `to_string()` on a `&str`, which should be `to_owned()`", + deprecation: None, + module: "strings", + }, Lint { name: "string_add", group: "restriction", @@ -2272,6 +2279,13 @@ vec![ deprecation: None, module: "strings", }, + Lint { + name: "string_to_string", + group: "restriction", + desc: "using `to_string()` on a `String`, which should be `clone()`", + deprecation: None, + module: "strings", + }, Lint { name: "struct_excessive_bools", group: "pedantic", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 4cbc5630d75..e1ee8dbca2c 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -1,5 +1,3 @@ -#[warn(clippy::str_to_string)] -#[warn(clippy::string_to_string)] #[warn(clippy::unstable_as_slice)] #[warn(clippy::unstable_as_mut_slice)] #[warn(clippy::misaligned_transmute)] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index a348d01d734..edbb891afe0 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,88 +1,76 @@ -error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated.rs:1:8 - | -LL | #[warn(clippy::str_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - -error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated.rs:2:8 - | -LL | #[warn(clippy::string_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` - --> $DIR/deprecated.rs:3:8 + --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` - --> $DIR/deprecated.rs:4:8 + --> $DIR/deprecated.rs:2:8 | LL | #[warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` - --> $DIR/deprecated.rs:5:8 + --> $DIR/deprecated.rs:3:8 | LL | #[warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint` - --> $DIR/deprecated.rs:6:8 + --> $DIR/deprecated.rs:4:8 | LL | #[warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value`` - --> $DIR/deprecated.rs:7:8 + --> $DIR/deprecated.rs:5:8 | LL | #[warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter`` - --> $DIR/deprecated.rs:8:8 + --> $DIR/deprecated.rs:6:8 | LL | #[warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels`` - --> $DIR/deprecated.rs:9:8 + --> $DIR/deprecated.rs:7:8 | LL | #[warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018` - --> $DIR/deprecated.rs:10:8 + --> $DIR/deprecated.rs:8:8 | LL | #[warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted to rustc and is now called `drop_bounds`` - --> $DIR/deprecated.rs:11:8 + --> $DIR/deprecated.rs:9:8 | LL | #[warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`` - --> $DIR/deprecated.rs:12:8 + --> $DIR/deprecated.rs:10:8 | LL | #[warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt`` - --> $DIR/deprecated.rs:13:8 + --> $DIR/deprecated.rs:11:8 | LL | #[warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` +error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` --> $DIR/deprecated.rs:1:8 | -LL | #[warn(clippy::str_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/deprecated_old.rs b/tests/ui/deprecated_old.rs index 2e5c5b7ead1..e89dca4fcfd 100644 --- a/tests/ui/deprecated_old.rs +++ b/tests/ui/deprecated_old.rs @@ -1,5 +1,3 @@ -#[warn(str_to_string)] -#[warn(string_to_string)] #[warn(unstable_as_slice)] #[warn(unstable_as_mut_slice)] #[warn(misaligned_transmute)] diff --git a/tests/ui/deprecated_old.stderr b/tests/ui/deprecated_old.stderr index ff3e9e8fcf3..2fe1facf0c7 100644 --- a/tests/ui/deprecated_old.stderr +++ b/tests/ui/deprecated_old.stderr @@ -1,40 +1,28 @@ -error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated_old.rs:1:8 - | -LL | #[warn(str_to_string)] - | ^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - -error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated_old.rs:2:8 - | -LL | #[warn(string_to_string)] - | ^^^^^^^^^^^^^^^^ - error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` - --> $DIR/deprecated_old.rs:3:8 + --> $DIR/deprecated_old.rs:1:8 | LL | #[warn(unstable_as_slice)] | ^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` - --> $DIR/deprecated_old.rs:4:8 + --> $DIR/deprecated_old.rs:2:8 | LL | #[warn(unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^ error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` - --> $DIR/deprecated_old.rs:5:8 + --> $DIR/deprecated_old.rs:3:8 | LL | #[warn(misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^ -error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` +error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` --> $DIR/deprecated_old.rs:1:8 | -LL | #[warn(str_to_string)] - | ^^^^^^^^^^^^^ +LL | #[warn(unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/str_to_string.rs b/tests/ui/str_to_string.rs new file mode 100644 index 00000000000..08f73402518 --- /dev/null +++ b/tests/ui/str_to_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::str_to_string)] + +fn main() { + let hello = "hello world".to_string(); + let msg = &hello[..]; + msg.to_string(); +} diff --git a/tests/ui/str_to_string.stderr b/tests/ui/str_to_string.stderr new file mode 100644 index 00000000000..b1f73eda5d2 --- /dev/null +++ b/tests/ui/str_to_string.stderr @@ -0,0 +1,19 @@ +error: `to_string()` called on a `&str` + --> $DIR/str_to_string.rs:4:17 + | +LL | let hello = "hello world".to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::str-to-string` implied by `-D warnings` + = help: consider using `.to_owned()` + +error: `to_string()` called on a `&str` + --> $DIR/str_to_string.rs:6:5 + | +LL | msg.to_string(); + | ^^^^^^^^^^^^^^^ + | + = help: consider using `.to_owned()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs new file mode 100644 index 00000000000..4c66855f709 --- /dev/null +++ b/tests/ui/string_to_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::string_to_string)] +#![allow(clippy::redundant_clone)] + +fn main() { + let mut message = String::from("Hello"); + let mut v = message.to_string(); +} diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr new file mode 100644 index 00000000000..1ebd17999bd --- /dev/null +++ b/tests/ui/string_to_string.stderr @@ -0,0 +1,11 @@ +error: `to_string()` called on a `String` + --> $DIR/string_to_string.rs:6:17 + | +LL | let mut v = message.to_string(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::string-to-string` implied by `-D warnings` + = help: consider using `.clone()` + +error: aborting due to previous error + From 85a17b53343b27ea1470243e4c01844a2997e860 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 25 Nov 2020 21:16:44 +0530 Subject: [PATCH 0993/1110] update README.md for specifying msrv --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 1da626b505d..080e8874427 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,30 @@ lints can be configured and the meaning of the variables. To deactivate the “for further information visit *lint-link*” message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. +### Specifying the minimum supported Rust version + +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by +specifying the minimum supported Rust version (msrv) in the clippy configuration file. + +```toml +msrv = "1.30.0" +``` + +The msrv can also be specified as an inner attribute, like below. + +```rust +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.30.0"] + +fn main() { + ... +} +``` + +Tilde/Caret version requirements(like `^1.0` or `~1.2`) can be specified as well. + +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. + ### Allowing/denying lints You can add options to your code to `allow`/`warn`/`deny` Clippy lints: From 94a6832f0bc6ec2d1de0f71e39332eca408551da Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 25 Nov 2020 22:09:50 +0530 Subject: [PATCH 0994/1110] update README.md --- README.md | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 080e8874427..e5cff0cc621 100644 --- a/README.md +++ b/README.md @@ -147,30 +147,6 @@ lints can be configured and the meaning of the variables. To deactivate the “for further information visit *lint-link*” message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. -### Specifying the minimum supported Rust version - -Projects that intend to support old versions of Rust can disable lints pertaining to newer features by -specifying the minimum supported Rust version (msrv) in the clippy configuration file. - -```toml -msrv = "1.30.0" -``` - -The msrv can also be specified as an inner attribute, like below. - -```rust -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.30.0"] - -fn main() { - ... -} -``` - -Tilde/Caret version requirements(like `^1.0` or `~1.2`) can be specified as well. - -Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. - ### Allowing/denying lints You can add options to your code to `allow`/`warn`/`deny` Clippy lints: @@ -218,6 +194,32 @@ cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. +### Specifying the minimum supported Rust version + +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by +specifying the minimum supported Rust version (MSRV) in the clippy configuration file. + +```toml +msrv = "1.30.0" +``` + +The MSRV can also be specified as an inner attribute, like below. + +```rust +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.30.0"] + +fn main() { + ... +} +``` + +Tilde/Caret version requirements(like `^1.0` or `~1.2`) can be specified as well. + +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. + +Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv) + ## Contributing If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md). From b2eb55b03ecd54595260320f4f08263033f813d1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 25 Nov 2020 20:37:32 +0100 Subject: [PATCH 0995/1110] Fix formatting in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e5cff0cc621..35683e87133 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ fn main() { } ``` -Tilde/Caret version requirements(like `^1.0` or `~1.2`) can be specified as well. +Tilde/Caret version requirements (like `^1.0` or `~1.2`) can be specified as well. Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. From 3b53de6b36081646315c0721638c4318c28b6982 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 21 Nov 2020 21:08:32 -0300 Subject: [PATCH 0996/1110] Fix rust-lang/rust#79255 - Incorrect try suggestion for unnecessary float literal cast ending in dot --- clippy_lints/src/types.rs | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 2 ++ tests/ui/unnecessary_cast_fixable.rs | 2 ++ tests/ui/unnecessary_cast_fixable.stderr | 32 ++++++++++++++++-------- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f0e10e374e1..f3cad66cbf3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1711,7 +1711,7 @@ fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &st expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", literal_str, cast_to), + format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 350da4965d1..7fbce58a82f 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -11,6 +11,8 @@ fn main() { let _ = -100_f32; let _ = -100_f64; let _ = -100_f64; + 100_f32; + 100_f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index ad2fb2e6289..a71363ea4d2 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -11,6 +11,8 @@ fn main() { let _ = -100 as f32; let _ = -100 as f64; let _ = -100_i32 as f64; + 100. as f32; + 100. as f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 5a210fc8909..3695a8f819c 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -36,59 +36,71 @@ error: casting integer literal to `f64` is unnecessary LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:14:5 + | +LL | 100. as f32; + | ^^^^^^^^^^^ help: try: `100_f32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:15:5 + | +LL | 100. as f64; + | ^^^^^^^^^^^ help: try: `100_f64` + error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:25:5 + --> $DIR/unnecessary_cast_fixable.rs:27:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:30:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:31:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:31:5 + --> $DIR/unnecessary_cast_fixable.rs:33:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:32:5 + --> $DIR/unnecessary_cast_fixable.rs:34:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:36:13 + --> $DIR/unnecessary_cast_fixable.rs:38:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:37:13 + --> $DIR/unnecessary_cast_fixable.rs:39:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors From 3bcc75d4462f11ee31260aa45b74a144b83e575f Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 26 Nov 2020 10:01:02 +0100 Subject: [PATCH 0997/1110] Remove mention of possibility to specify the MSRV with a tilde/caret --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 35683e87133..3d7ad38ea6c 100644 --- a/README.md +++ b/README.md @@ -214,8 +214,6 @@ fn main() { } ``` -Tilde/Caret version requirements (like `^1.0` or `~1.2`) can be specified as well. - Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv) From 6eb2c27bcc8760949280f266a52dcc6bb3ca6955 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 26 Nov 2020 10:15:00 +0100 Subject: [PATCH 0998/1110] Note that it is possible to omit the patch version --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d7ad38ea6c..fddf0614a0b 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ cargo clippy -- -W clippy::lint_name ``` This also works with lint groups. For example you -can run Clippy with warnings for all lints enabled: +can run Clippy with warnings for all lints enabled: ```terminal cargo clippy -- -W clippy::pedantic ``` @@ -214,6 +214,9 @@ fn main() { } ``` +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30` +is equivalent to `msrv = 1.30.0`. + Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv) From cb6a654b75ca095aee9ebbe5cf05fc48df5b9b50 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Thu, 26 Nov 2020 20:07:50 +0000 Subject: [PATCH 0999/1110] Added known problem to comparison_chain docs --- clippy_lints/src/comparison_chain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 99f161a0510..ae1143b2c50 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -12,7 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get /// repetitive /// - /// **Known problems:** None. + /// **Known problems:** The match statement may be slower due to the compiler + /// not inlining the call to cmp. See issue #5354 /// /// **Example:** /// ```rust,ignore From e91d15f42d761c1a2f161a2b65deee06a7f472ab Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 27 Nov 2020 10:32:44 +0900 Subject: [PATCH 1000/1110] cargo dev fmt --- clippy_lints/src/attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 15505fd79f4..3edbe723922 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -5,7 +5,6 @@ use crate::utils::{ span_lint_and_sugg, span_lint_and_then, without_block_comments, }; use if_chain::if_chain; -use rustc_span::lev_distance::find_best_match_for_name; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ @@ -15,6 +14,7 @@ use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext, use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::{Symbol, SymbolStr}; From 82a7068007a1490b43a2eb4e70e0f70de384a9ae Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Sat, 21 Nov 2020 12:28:53 +0100 Subject: [PATCH 1001/1110] Trigger modulo_one lint also on -1. --- clippy_lints/src/misc.rs | 30 ++++++++++++++------- tests/ui/modulo_one.rs | 11 +++++++- tests/ui/modulo_one.stderr | 54 ++++++++++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 308e92057b7..f16feb9b1ba 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, SpanlessEq, + span_lint_and_then, span_lint_hir_and_then, SpanlessEq, unsext, }; declare_clippy_lint! { @@ -139,12 +139,14 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for getting the remainder of a division by one. + /// **What it does:** Checks for getting the remainder of a division by one or minus + /// one. /// - /// **Why is this bad?** The result can only ever be zero. No one will write - /// such code deliberately, unless trying to win an Underhanded Rust - /// Contest. Even for that contest, it's probably a bad idea. Use something more - /// underhanded. + /// **Why is this bad?** The result for a divisor of one can only ever be zero; for + /// minus one it can cause panic/overflow (if the left operand is the minimal value of + /// the respective integer type) or results in zero. No one will write such code + /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that + /// contest, it's probably a bad idea. Use something more underhanded. /// /// **Known problems:** None. /// @@ -152,10 +154,11 @@ declare_clippy_lint! { /// ```rust /// # let x = 1; /// let a = x % 1; + /// let a = x % -1; /// ``` pub MODULO_ONE, correctness, - "taking a number modulo 1, which always returns 0" + "taking a number modulo +/-1, which can either panic/overflow or always returns 0" } declare_clippy_lint! { @@ -429,8 +432,17 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); - } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { - span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } else if op == BinOpKind::Rem { + if is_integer_const(cx, right, 1) { + span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } + + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { + if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { + span_lint(cx, MODULO_ONE, expr.span, + "any number modulo -1 will panic/overflow or result in 0"); + } + }; } }, _ => {}, diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index cc8c8e7cdae..678a312f66e 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -2,13 +2,22 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation)] static STATIC_ONE: usize = 2 - 1; +static STATIC_NEG_ONE: i64 = 1 - 2; fn main() { 10 % 1; + 10 % -1; 10 % 2; + i32::MIN % (-1); // also caught by rustc const ONE: u32 = 1 * 1; + const NEG_ONE: i64 = 1 - 2; + const INT_MIN: i64 = i64::MIN; 2 % ONE; - 5 % STATIC_ONE; + 5 % STATIC_ONE; // NOT caught by lint + 2 % NEG_ONE; + 5 % STATIC_NEG_ONE; // NOT caught by lint + INT_MIN % NEG_ONE; // also caught by rustc + INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc } diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 6bee68360b6..2b2c6997338 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -1,13 +1,45 @@ +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:11:5 + | +LL | i32::MIN % (-1); // also caught by rustc + | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow + | + = note: `#[deny(arithmetic_overflow)]` on by default + +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:21:5 + | +LL | INT_MIN % NEG_ONE; // also caught by rustc + | ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow + +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:22:5 + | +LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc + | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow + error: any number modulo 1 will be 0 - --> $DIR/modulo_one.rs:7:5 + --> $DIR/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ | = note: `-D clippy::modulo-one` implied by `-D warnings` +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:9:5 + | +LL | 10 % -1; + | ^^^^^^^ + +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:11:5 + | +LL | i32::MIN % (-1); // also caught by rustc + | ^^^^^^^^^^^^^^^ + error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:10:22 + --> $DIR/modulo_one.rs:13:22 | LL | const ONE: u32 = 1 * 1; | ^^^^^ @@ -15,16 +47,28 @@ LL | const ONE: u32 = 1 * 1; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:10:22 + --> $DIR/modulo_one.rs:13:22 | LL | const ONE: u32 = 1 * 1; | ^^^^^ error: any number modulo 1 will be 0 - --> $DIR/modulo_one.rs:12:5 + --> $DIR/modulo_one.rs:17:5 | LL | 2 % ONE; | ^^^^^^^ -error: aborting due to 4 previous errors +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:19:5 + | +LL | 2 % NEG_ONE; + | ^^^^^^^^^^^ + +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:21:5 + | +LL | INT_MIN % NEG_ONE; // also caught by rustc + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors From e42a18f02a45ffc12016a71bb4a210aa62af9a24 Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Sat, 21 Nov 2020 12:38:21 +0100 Subject: [PATCH 1002/1110] Run `cargo dev fmt`. --- clippy_lints/src/misc.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index f16feb9b1ba..b527b2cc1cb 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, SpanlessEq, unsext, + span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -439,8 +439,12 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint(cx, MODULO_ONE, expr.span, - "any number modulo -1 will panic/overflow or result in 0"); + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); } }; } From b8226320735bc8e3f699cc177be638433ed396d9 Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Mon, 23 Nov 2020 10:18:27 +0100 Subject: [PATCH 1003/1110] Factor out `check_binary` from function `check_expr`. --- clippy_lints/src/misc.rs | 140 ++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 67 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b527b2cc1cb..0512d74c7b1 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -381,73 +381,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { return; }, ExprKind::Binary(ref cmp, ref left, ref right) => { - let op = cmp.node; - if op.is_comparison() { - check_nan(cx, left, expr); - check_nan(cx, right, expr); - check_to_owned(cx, left, right, true); - check_to_owned(cx, right, left, false); - } - if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { - if is_allowed(cx, left) || is_allowed(cx, right) { - return; - } - - // Allow comparing the results of signum() - if is_signum(cx, left) && is_signum(cx, right) { - return; - } - - if let Some(name) = get_item_name(cx, expr) { - let name = name.as_str(); - if name == "eq" - || name == "ne" - || name == "is_nan" - || name.starts_with("eq_") - || name.ends_with("_eq") - { - return; - } - } - let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); - let (lint, msg) = get_lint_and_message( - is_named_constant(cx, left) || is_named_constant(cx, right), - is_comparing_arrays, - ); - span_lint_and_then(cx, lint, expr.span, msg, |diag| { - let lhs = Sugg::hir(cx, left, ".."); - let rhs = Sugg::hir(cx, right, ".."); - - if !is_comparing_arrays { - diag.span_suggestion( - expr.span, - "consider comparing them within some margin of error", - format!( - "({}).abs() {} error_margin", - lhs - rhs, - if op == BinOpKind::Eq { '<' } else { '>' } - ), - Applicability::HasPlaceholders, // snippet - ); - } - diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); - }); - } else if op == BinOpKind::Rem { - if is_integer_const(cx, right, 1) { - span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); - } - - if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { - if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint( - cx, - MODULO_ONE, - expr.span, - "any number modulo -1 will panic/overflow or result in 0", - ); - } - }; - } + check_binary(cx, expr, cmp, left, right); + return; }, _ => {}, } @@ -760,3 +695,74 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) } } } + +fn check_binary( + cx: &LateContext<'a>, + expr: &Expr<'_>, + cmp: &rustc_span::source_map::Spanned, + left: &'a Expr<'_>, + right: &'a Expr<'_>, +) { + let op = cmp.node; + if op.is_comparison() { + check_nan(cx, left, expr); + check_nan(cx, right, expr); + check_to_owned(cx, left, right, true); + check_to_owned(cx, right, left, false); + } + if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { + if is_allowed(cx, left) || is_allowed(cx, right) { + return; + } + + // Allow comparing the results of signum() + if is_signum(cx, left) && is_signum(cx, right) { + return; + } + + if let Some(name) = get_item_name(cx, expr) { + let name = name.as_str(); + if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { + return; + } + } + let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); + let (lint, msg) = get_lint_and_message( + is_named_constant(cx, left) || is_named_constant(cx, right), + is_comparing_arrays, + ); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let lhs = Sugg::hir(cx, left, ".."); + let rhs = Sugg::hir(cx, right, ".."); + + if !is_comparing_arrays { + diag.span_suggestion( + expr.span, + "consider comparing them within some margin of error", + format!( + "({}).abs() {} error_margin", + lhs - rhs, + if op == BinOpKind::Eq { '<' } else { '>' } + ), + Applicability::HasPlaceholders, // snippet + ); + } + diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); + }); + } else if op == BinOpKind::Rem { + if is_integer_const(cx, right, 1) { + span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } + + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { + if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); + } + }; + } +} From 0387981f2b5e41c982ec4a1b102f0c54997361ff Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 17 Oct 2020 19:48:40 +0200 Subject: [PATCH 1004/1110] Add --no-deps command-line argument --- clippy_workspace_tests/path_dep/Cargo.toml | 3 + clippy_workspace_tests/path_dep/src/lib.rs | 6 ++ clippy_workspace_tests/subcrate/Cargo.toml | 3 + src/driver.rs | 35 ++++++----- tests/dogfood.rs | 71 +++++++++++++++++++++- 5 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 clippy_workspace_tests/path_dep/Cargo.toml create mode 100644 clippy_workspace_tests/path_dep/src/lib.rs diff --git a/clippy_workspace_tests/path_dep/Cargo.toml b/clippy_workspace_tests/path_dep/Cargo.toml new file mode 100644 index 00000000000..85a91cd2dec --- /dev/null +++ b/clippy_workspace_tests/path_dep/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "path_dep" +version = "0.1.0" diff --git a/clippy_workspace_tests/path_dep/src/lib.rs b/clippy_workspace_tests/path_dep/src/lib.rs new file mode 100644 index 00000000000..35ce524f2b1 --- /dev/null +++ b/clippy_workspace_tests/path_dep/src/lib.rs @@ -0,0 +1,6 @@ +#![deny(clippy::empty_loop)] + +#[cfg(feature = "primary_package_test")] +pub fn lint_me() { + loop {} +} diff --git a/clippy_workspace_tests/subcrate/Cargo.toml b/clippy_workspace_tests/subcrate/Cargo.toml index 83ea5868160..45362c11b85 100644 --- a/clippy_workspace_tests/subcrate/Cargo.toml +++ b/clippy_workspace_tests/subcrate/Cargo.toml @@ -1,3 +1,6 @@ [package] name = "subcrate" version = "0.1.0" + +[dependencies] +path_dep = { path = "../path_dep" } diff --git a/src/driver.rs b/src/driver.rs index ef31c72481a..bbe9ce73936 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -277,27 +277,34 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; + let mut no_deps = false; + let clippy_args = env::var("CLIPPY_ARGS") + .unwrap_or_default() + .split("__CLIPPY_HACKERY__") + .filter_map(|s| match s { + "" => None, + "--no-deps" => { + no_deps = true; + None + }, + _ => Some(s.to_string()), + }) + .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) + .collect::>(); + // this check ensures that dependencies are built but not linted and the final // crate is linted but not built - let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true") - || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); + let clippy_disabled = env::var("CLIPPY_TESTS").map_or(false, |val| val != "true") + || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some() + || no_deps && env::var("CARGO_PRIMARY_PACKAGE").is_err(); - if clippy_enabled { - args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); - if let Ok(extra_args) = env::var("CLIPPY_ARGS") { - args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { - if s.is_empty() { - None - } else { - Some(s.to_string()) - } - })); - } + if !clippy_disabled { + args.extend(clippy_args); } let mut clippy = ClippyCallbacks; let mut default = DefaultCallbacks; let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = - if clippy_enabled { &mut clippy } else { &mut default }; + if clippy_disabled { &mut default } else { &mut clippy }; rustc_driver::RunCompiler::new(&args, callbacks).run() })) } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 48e0478f169..b166a6b7c1f 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -3,7 +3,7 @@ #![feature(once_cell)] use std::lazy::SyncLazy; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; mod cargo; @@ -41,12 +41,77 @@ fn dogfood_clippy() { #[test] fn dogfood_subprojects() { + fn test_no_deps_ignores_path_deps_in_workspaces() { + fn clean(cwd: &Path, target_dir: &Path) { + Command::new("cargo") + .current_dir(cwd) + .env("CARGO_TARGET_DIR", target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + } + + if cargo::is_rustc_test_suite() { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("dogfood"); + let cwd = root.join("clippy_workspace_tests"); + + // Make sure we start with a clean state + clean(&cwd, &target_dir); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("--no-deps") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + // Make sure we start with a clean state + clean(&cwd, &target_dir); + + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + } + // run clippy on remaining subprojects and fail the test if lint warnings are reported if cargo::is_rustc_test_suite() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // NOTE: `path_dep` crate is omitted on purpose here for d in &[ "clippy_workspace_tests", "clippy_workspace_tests/src", @@ -72,4 +137,8 @@ fn dogfood_subprojects() { assert!(output.status.success()); } + + // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the + // same time, so we test this immediately after the dogfood for workspaces. + test_no_deps_ignores_path_deps_in_workspaces(); } From 192ccfb4efc0a38378f4564b26e6033dec432bdb Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 17 Oct 2020 19:55:03 +0200 Subject: [PATCH 1005/1110] Update README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index fddf0614a0b..aaa55e11c7d 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,22 @@ Note that this is still experimental and only supported on the nightly channel: cargo clippy --fix -Z unstable-options ``` +#### Workspaces + +All the usual workspace options should work with Clippy. For example the following command +will run Clippy on the `example` crate: + +```terminal +cargo clippy -p example +``` + +As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies. +If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this: + +```terminal +cargo clippy -p example -- --no-deps +``` + ### Running Clippy from the command line without installing it To have cargo compile your crate with Clippy without Clippy installation From 7eda421e9629a717d31ec03d12b4befd03f5fb50 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 25 Nov 2020 15:02:47 +0100 Subject: [PATCH 1006/1110] Apply suggestion regarding clippy_enabled bool --- src/driver.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index bbe9ce73936..03381106de1 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -182,6 +182,7 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option Date: Fri, 27 Nov 2020 16:53:30 +0100 Subject: [PATCH 1007/1110] Make --fix imply --no-deps --- src/main.rs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6739a4cf224..ea06743394d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,7 +62,7 @@ struct ClippyCmd { unstable_options: bool, cargo_subcommand: &'static str, args: Vec, - clippy_args: String, + clippy_args: Vec, } impl ClippyCmd { @@ -99,7 +99,10 @@ impl ClippyCmd { args.insert(0, "+nightly".to_string()); } - let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect(); + let mut clippy_args: Vec = old_args.collect(); + if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { + clippy_args.push("--no-deps".into()); + } ClippyCmd { unstable_options, @@ -147,10 +150,15 @@ impl ClippyCmd { fn into_std_cmd(self) -> Command { let mut cmd = Command::new("cargo"); + let clippy_args: String = self + .clippy_args + .iter() + .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) + .collect(); cmd.env(self.path_env(), Self::path()) .envs(ClippyCmd::target_dir()) - .env("CLIPPY_ARGS", self.clippy_args) + .env("CLIPPY_ARGS", clippy_args) .arg(self.cargo_subcommand) .args(&self.args); @@ -201,6 +209,24 @@ mod tests { assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options"))); } + #[test] + fn fix_implies_no_deps() { + let args = "cargo clippy --fix -Zunstable-options" + .split_whitespace() + .map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps")); + } + + #[test] + fn no_deps_not_duplicated_with_fix() { + let args = "cargo clippy --fix -Zunstable-options -- --no-deps" + .split_whitespace() + .map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1); + } + #[test] fn check() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); From 952b731fb9134e44e4c99bae46e6a917c944e77e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 27 Nov 2020 18:04:30 +0100 Subject: [PATCH 1008/1110] Reword bitrotten comment --- src/driver.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 03381106de1..e490ee54c0b 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -293,8 +293,11 @@ pub fn main() { .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) .collect::>(); - // this check ensures that dependencies are built but not linted and the final - // crate is linted but not built + // We enable Clippy if one of the following conditions is met + // - IF Clippy is run on its test suite OR + // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN + // - IF `--no-deps` is not set (`!no_deps`) OR + // - IF `--no-deps` is set and Clippy is run on the specified primary package let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true"); let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); From af1cc5c91131e0ec30f0f34691a5e635350295a1 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sat, 7 Nov 2020 16:00:42 -0700 Subject: [PATCH 1009/1110] add suspicious_operation_groupings lint run `cargo dev new_lint --category correctness --name suspicious_chained_operators --pass early` add (currently failing) tests for suspicious_chained_operators add some tests to answer a question that came up during implementation write usage code for functions we'll need to find or create Complete left-right tracking TODO get it compiling with several `todo!` invocations. refactor to a set of incomplete functions that don't expect to be able to edit a `Span` create placeholder for `suggestion_with_swapped_ident` function and correct some comments add `inside_larger_boolean_expression` test fill out `get_ident` and `suggestion_with_swapped_ident` Implementi the `IdentIter` start on implementing the `IdentIter` handle the `ExprKind::Path` case in `IdentIter` on second thought, make the iterator type dynamic so we don't need an explicit type for each one we will need handle `ExprKind::MacCall` in `IdentIter` Try handling `box x` expressions restructure `IdentIter` set `self.done` when returning `None` Handle `ExprKind::Array` reduce duplication with a macro that we expect to use several more times handle ExprKind::Call add `new_p` convenience method handle `MethodCall` handle `Tup` and `Binary` handle `Unary` simplify by not returning an additional `Expr` from the `IdentIter` add cross product test against false positives rename suspicious_chained_operators to suspicious_operation_groupings within files For the record, the exact commands run were: find . -type f -name "*.md" -exec sed -i 's/suspicious_chained_operators/suspicious_operation_groupings/g' {} + find . -type f -name "*.rs" -exec sed -i 's/suspicious_chained_operators/suspicious_operation_groupings/g' {} + find . -type f -name "*.rs" -exec sed -i 's/SUSPICIOUS_CHAINED_OPERATORS/SUSPICIOUS_OPERATION_GROUPINGS/g' {} + find . -type f -name "*.rs" -exec sed -i 's/SuspiciousChainedOperators/SuspiciousOperationGroupings/g' {} + Also: rename file to match module name rename test file to match lint name start implementing `IdentDifference` creation add `IdentIter` utility use `ident_iter::IdentIter` fix bug in `suggestion_with_swapped_ident` add `inside_if_statements` test implement `Add` `todo`s register `SuspiciousOperationGroupings` lint pass fill in `chained_binops`, and fill in a stopgap version of `ident_difference_expr`, but then notice that the lint does not seem to ever be run in the tests run `cargo dev update_lints` and not that the `suspicious_operation_groupings` lint still does not seem to be run fix base index incrementing bug fix paired_identifiers bug, and remove ident from `Single` change help prefix and note our first successful lint messages! add odd_number_of_pairs test get the `non_boolean_operators` test passing, with two copies of the error message extract `is_useless_with_eq_exprs` so we can know when `eq_op` will already handle something add `not_caught_by_eq_op` tests since `s1.b * s1.b` was (reasonably) not caught by `eq_op` cover the case where the change should be made on either side of the expression with `not_caught_by_eq_op` tests produce the expected suggestion on the `not_caught_by_eq_op_middle_change_left` test confirm that the previous tests still pass and update references fix early continue bug and get `not_caught_by_eq_op_middle_change_right` passing note that `not_caught_by_eq_op_start` already passes fix bugs based on misunderstanding of what `Iterator::skip` does, and note that `not_caught_by_eq_op_end` now passes add several parens tests and make some of them pass handle parens inside `chained_binops_helper` and note that this makes several tests pass get `inside_larger_boolean_expression_with_unsorted_ops` test passing by extracting out `check_same_op_binops` function also run `cargo dev fmt` note that `inside_function_call` already passes add another `if_statement` test remove the matching op requirement, making `inside_larger_boolean_expression_with_unsorted_ops` pass prevent non-change suggestions from being emitted get the `Nested` tests passing, and remove apparently false note about eq_op add a test to justify comment in `ident_difference_expr_with_base_location` but find that the failure mode seems different than expected complete `todo` making `do_not_give_bad_suggestions_for_this_unusual_expr` pass and add some more tests that already pass add test to `eq_op` note that `inside_fn_with_similar_expression` already passes fix `inside_an_if_statement` and note that it already passes attempt to implement if statement extraction and notice that we don't seem to handle unary ops correctly add `maximum_unary_minus_right_tree` test and make it pass add two tests and note one of them passes filter out unary operations in several places, and find that the issue seems to be that we don't currently recognize the error in `multiple_comparison_types_and_unary_minus` even so. remove filtering that was causing bad suggestions remove tests that were deemed too much for now run `cargo dev fmt` correct eq_op post-merge fill out the description and delete debugging code run `cargo dev update_lints` update eq_op references add parens to work around rustfmt issue #3666 and run rustfmt https://github.com/rust-lang/rustfmt/issues/3666#issuecomment-714612257 update references after formatting fix dogfood issues fix multi-cursor edit fix missed dogfood error fix more dogfood pedantic issues, including function length even more nesting insert hidden definition of Vec3 so docs compile add spaces to second struct def reword test description comment Co-authored-by: llogiq add local `use BinOpKind::*;` Apply suggestions from code review Co-authored-by: llogiq switch `SUSPICIOUS_OPERATION_GROUPINGS` to a style lint run `cargo dev update_lints` put both usages of `op_types` in the same closure to satisfy `borrowck` fix compile error --- CHANGELOG.md | 1 + clippy_lints/src/eq_op.rs | 27 +- clippy_lints/src/lib.rs | 5 + .../src/suspicious_operation_groupings.rs | 693 ++++++++++++++++++ clippy_lints/src/utils/ast_utils.rs | 11 + .../src/utils/ast_utils/ident_iter.rs | 45 ++ tests/ui/eq_op.rs | 9 + tests/ui/eq_op.stderr | 10 +- tests/ui/suspicious_operation_groupings.rs | 207 ++++++ .../ui/suspicious_operation_groupings.stderr | 166 +++++ 10 files changed, 1150 insertions(+), 24 deletions(-) create mode 100644 clippy_lints/src/suspicious_operation_groupings.rs create mode 100644 clippy_lints/src/utils/ast_utils/ident_iter.rs create mode 100644 tests/ui/suspicious_operation_groupings.rs create mode 100644 tests/ui/suspicious_operation_groupings.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b9e4b0e6704..e76a781f13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2073,6 +2073,7 @@ Released 2018-09-13 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 3201adbf9a0..6308f6e2e7e 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,10 +1,10 @@ use crate::utils::{ - eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, - span_lint_and_then, + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, + multispan_sugg, snippet, span_lint, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && eq_expr_value(cx, left, right) { + if is_useless_with_eq_exprs(higher::binop(op.node)) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, @@ -245,22 +245,3 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } - -fn is_valid_operator(op: BinOp) -> bool { - matches!( - op.node, - BinOpKind::Sub - | BinOpKind::Div - | BinOpKind::Eq - | BinOpKind::Lt - | BinOpKind::Le - | BinOpKind::Gt - | BinOpKind::Ge - | BinOpKind::Ne - | BinOpKind::And - | BinOpKind::Or - | BinOpKind::BitXor - | BinOpKind::BitAnd - | BinOpKind::BitOr - ) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 67a3a3fcf48..6eb5f6a7f48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -308,6 +308,7 @@ mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; mod strings; +mod suspicious_operation_groupings; mod suspicious_trait_impl; mod swap; mod tabs_in_doc_comments; @@ -834,6 +835,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &strings::STRING_LIT_AS_BYTES, &strings::STRING_TO_STRING, &strings::STR_TO_STRING, + &suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &swap::ALMOST_SWAPPED, @@ -1066,6 +1068,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box types::UnitArg); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); + store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); store.register_late_pass(|| box map_unit_fn::MapUnit); store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); @@ -1547,6 +1550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1698,6 +1702,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), 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), diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs new file mode 100644 index 00000000000..cccd24ccf94 --- /dev/null +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -0,0 +1,693 @@ +use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use core::ops::{Add, AddAssign}; +use if_chain::if_chain; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for unlikely usages of binary operators that are almost + /// certainly typos and/or copy/paste errors, given the other usages + /// of binary operators nearby. + /// **Why is this bad?** + /// They are probably bugs and if they aren't then they look like bugs + /// and you should add a comment explaining why you are doing such an + /// odd set of operations. + /// **Known problems:** + /// There may be some false positives if you are trying to do something + /// unusual that happens to look like a typo. + /// + /// **Example:** + /// + /// ```rust + /// struct Vec3 { + /// x: f64, + /// y: f64, + /// z: f64, + /// } + /// + /// impl Eq for Vec3 {} + /// + /// impl PartialEq for Vec3 { + /// fn eq(&self, other: &Self) -> bool { + /// // This should trigger the lint because `self.x` is compared to `other.y` + /// self.x == other.y && self.y == other.y && self.z == other.z + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// # struct Vec3 { + /// # x: f64, + /// # y: f64, + /// # z: f64, + /// # } + /// // same as above except: + /// impl PartialEq for Vec3 { + /// fn eq(&self, other: &Self) -> bool { + /// // Note we now compare other.x to self.x + /// self.x == other.x && self.y == other.y && self.z == other.z + /// } + /// } + /// ``` + pub SUSPICIOUS_OPERATION_GROUPINGS, + style, + "groupings of binary operations that look suspiciously like typos" +} + +declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]); + +impl EarlyLintPass for SuspiciousOperationGroupings { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + + if let Some(binops) = extract_related_binops(&expr.kind) { + check_binops(cx, &binops.iter().collect::>()); + + let mut op_types = Vec::with_capacity(binops.len()); + // We could use a hashmap, etc. to avoid being O(n*m) here, but + // we want the lints to be emitted in a consistent order. Besides, + // m, (the number of distinct `BinOpKind`s in `binops`) + // will often be small, and does have an upper limit. + binops.iter().map(|b| b.op).for_each(|op| { + if !op_types.contains(&op) { + op_types.push(op); + } + }); + + for op_type in op_types { + let ops: Vec<_> = binops.iter().filter(|b| b.op == op_type).collect(); + + check_binops(cx, &ops); + } + } + } +} + +fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) { + let binop_count = binops.len(); + if binop_count < 2 { + // Single binary operation expressions would likely be false + // positives. + return; + } + + let mut one_ident_difference_count = 0; + let mut no_difference_info = None; + let mut double_difference_info = None; + let mut expected_ident_loc = None; + + let mut paired_identifiers = FxHashSet::default(); + + for (i, BinaryOp { left, right, op, .. }) in binops.iter().enumerate() { + match ident_difference_expr(left, right) { + IdentDifference::NoDifference => { + if is_useless_with_eq_exprs(*op) { + // The `eq_op` lint should catch this in this case. + return; + } + + no_difference_info = Some(i); + }, + IdentDifference::Single(ident_loc) => { + one_ident_difference_count += 1; + if let Some(previous_expected) = expected_ident_loc { + if previous_expected != ident_loc { + // This expression doesn't match the form we're + // looking for. + return; + } + } else { + expected_ident_loc = Some(ident_loc); + } + + // If there was only a single difference, all other idents + // must have been the same, and thus were paired. + for id in skip_index(IdentIter::from(*left), ident_loc.index) { + paired_identifiers.insert(id); + } + }, + IdentDifference::Double(ident_loc1, ident_loc2) => { + double_difference_info = Some((i, ident_loc1, ident_loc2)); + }, + IdentDifference::Multiple | IdentDifference::NonIdent => { + // It's too hard to know whether this is a bug or not. + return; + }, + } + } + + let mut applicability = Applicability::MachineApplicable; + + if let Some(expected_loc) = expected_ident_loc { + match (no_difference_info, double_difference_info) { + (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc), + (None, Some((double_difference_index, ident_loc1, ident_loc2))) => { + if_chain! { + if one_ident_difference_count == binop_count - 1; + if let Some(binop) = binops.get(double_difference_index); + then { + let changed_loc = if ident_loc1 == expected_loc { + ident_loc2 + } else if ident_loc2 == expected_loc { + ident_loc1 + } else { + // This expression doesn't match the form we're + // looking for. + return; + }; + + if let Some(sugg) = ident_swap_sugg( + cx, + &paired_identifiers, + binop, + changed_loc, + &mut applicability, + ) { + emit_suggestion( + cx, + binop.span, + sugg, + applicability, + ); + } + } + } + }, + _ => {}, + } + } +} + +fn attempt_to_emit_no_difference_lint( + cx: &EarlyContext<'_>, + binops: &[&BinaryOp<'_>], + i: usize, + expected_loc: IdentLocation, +) { + if let Some(binop) = binops.get(i).cloned() { + // We need to try and figure out which identifier we should + // suggest using instead. Since there could be multiple + // replacement candidates in a given expression, and we're + // just taking the first one, we may get some bad lint + // messages. + let mut applicability = Applicability::MaybeIncorrect; + + // We assume that the correct ident is one used elsewhere in + // the other binops, in a place that there was a single + // difference between idents before. + let old_left_ident = get_ident(binop.left, expected_loc); + let old_right_ident = get_ident(binop.right, expected_loc); + + for b in skip_index(binops.iter(), i) { + if_chain! { + if let (Some(old_ident), Some(new_ident)) = + (old_left_ident, get_ident(b.left, expected_loc)); + if old_ident != new_ident; + if let Some(sugg) = suggestion_with_swapped_ident( + cx, + binop.left, + expected_loc, + new_ident, + &mut applicability, + ); + then { + emit_suggestion( + cx, + binop.span, + replace_left_sugg(cx, &binop, &sugg, &mut applicability), + applicability, + ); + return; + } + } + + if_chain! { + if let (Some(old_ident), Some(new_ident)) = + (old_right_ident, get_ident(b.right, expected_loc)); + if old_ident != new_ident; + if let Some(sugg) = suggestion_with_swapped_ident( + cx, + binop.right, + expected_loc, + new_ident, + &mut applicability, + ); + then { + emit_suggestion( + cx, + binop.span, + replace_right_sugg(cx, &binop, &sugg, &mut applicability), + applicability, + ); + return; + } + } + } + } +} + +fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicability: Applicability) { + span_lint_and_sugg( + cx, + SUSPICIOUS_OPERATION_GROUPINGS, + span, + "This sequence of operators looks suspiciously like a bug.", + "I think you meant", + sugg, + applicability, + ) +} + +fn ident_swap_sugg( + cx: &EarlyContext<'_>, + paired_identifiers: &FxHashSet, + binop: &BinaryOp<'_>, + location: IdentLocation, + applicability: &mut Applicability, +) -> Option { + let left_ident = get_ident(&binop.left, location)?; + let right_ident = get_ident(&binop.right, location)?; + + let sugg = match ( + paired_identifiers.contains(&left_ident), + paired_identifiers.contains(&right_ident), + ) { + (true, true) | (false, false) => { + // We don't have a good guess of what ident should be + // used instead, in these cases. + *applicability = Applicability::MaybeIncorrect; + + // We arbitraily choose one side to suggest changing, + // since we don't have a better guess. If the user + // ends up duplicating a clause, the `logic_bug` lint + // should catch it. + + let right_suggestion = + suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + + replace_right_sugg(cx, binop, &right_suggestion, applicability) + }, + (false, true) => { + // We haven't seen a pair involving the left one, so + // it's probably what is wanted. + + let right_suggestion = + suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + + replace_right_sugg(cx, binop, &right_suggestion, applicability) + }, + (true, false) => { + // We haven't seen a pair involving the right one, so + // it's probably what is wanted. + let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?; + + replace_left_sugg(cx, binop, &left_suggestion, applicability) + }, + }; + + Some(sugg) +} + +fn replace_left_sugg( + cx: &EarlyContext<'_>, + binop: &BinaryOp<'_>, + left_suggestion: &str, + applicability: &mut Applicability, +) -> String { + format!( + "{} {} {}", + left_suggestion, + binop.op.to_string(), + snippet_with_applicability(cx, binop.right.span, "..", applicability), + ) +} + +fn replace_right_sugg( + cx: &EarlyContext<'_>, + binop: &BinaryOp<'_>, + right_suggestion: &str, + applicability: &mut Applicability, +) -> String { + format!( + "{} {} {}", + snippet_with_applicability(cx, binop.left.span, "..", applicability), + binop.op.to_string(), + right_suggestion, + ) +} + +#[derive(Clone, Debug)] +struct BinaryOp<'exprs> { + op: BinOpKind, + span: Span, + left: &'exprs Expr, + right: &'exprs Expr, +} + +impl BinaryOp<'exprs> { + fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { + Self { op, span, left, right } + } +} + +fn strip_non_ident_wrappers(expr: &Expr) -> &Expr { + let mut output = expr; + loop { + output = match &output.kind { + ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner, + _ => { + return output; + }, + }; + } +} + +fn extract_related_binops(kind: &ExprKind) -> Option>> { + append_opt_vecs(chained_binops(kind), if_statment_binops(kind)) +} + +fn if_statment_binops(kind: &ExprKind) -> Option>> { + match kind { + ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind), + ExprKind::Paren(ref e) => if_statment_binops(&e.kind), + ExprKind::Block(ref block, _) => { + let mut output = None; + for stmt in &block.stmts { + match stmt.kind { + StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => { + output = append_opt_vecs(output, if_statment_binops(&e.kind)); + }, + _ => {}, + } + } + output + }, + _ => None, + } +} + +fn append_opt_vecs(target_opt: Option>, source_opt: Option>) -> Option> { + match (target_opt, source_opt) { + (Some(mut target), Some(mut source)) => { + target.reserve(source.len()); + for op in source.drain(..) { + target.push(op); + } + Some(target) + }, + (Some(v), None) | (None, Some(v)) => Some(v), + (None, None) => None, + } +} + +fn chained_binops(kind: &ExprKind) -> Option>> { + match kind { + ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer), + ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind), + _ => None, + } +} + +fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option>> { + match (&left_outer.kind, &right_outer.kind) { + ( + ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), + ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e), + ) => chained_binops_helper(left_e, right_e), + (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer), + (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => { + chained_binops_helper(left_outer, right_e) + }, + ( + ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right), + ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right), + ) => match ( + chained_binops_helper(left_left, left_right), + chained_binops_helper(right_left, right_right), + ) { + (Some(mut left_ops), Some(mut right_ops)) => { + left_ops.reserve(right_ops.len()); + for op in right_ops.drain(..) { + left_ops.push(op); + } + Some(left_ops) + }, + (Some(mut left_ops), _) => { + left_ops.push(BinaryOp::new(*right_op, right_outer.span, (right_left, right_right))); + Some(left_ops) + }, + (_, Some(mut right_ops)) => { + right_ops.insert(0, BinaryOp::new(*left_op, left_outer.span, (left_left, left_right))); + Some(right_ops) + }, + (None, None) => Some(vec![ + BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)), + BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)), + ]), + }, + _ => None, + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +struct IdentLocation { + index: usize, +} + +impl Add for IdentLocation { + type Output = IdentLocation; + + fn add(self, other: Self) -> Self::Output { + Self { + index: self.index + other.index, + } + } +} + +impl AddAssign for IdentLocation { + fn add_assign(&mut self, other: Self) { + *self = *self + other + } +} + +#[derive(Clone, Copy, Debug)] +enum IdentDifference { + NoDifference, + Single(IdentLocation), + Double(IdentLocation, IdentLocation), + Multiple, + NonIdent, +} + +impl Add for IdentDifference { + type Output = IdentDifference; + + fn add(self, other: Self) -> Self::Output { + match (self, other) { + (Self::NoDifference, output) | (output, Self::NoDifference) => output, + (Self::Multiple, _) + | (_, Self::Multiple) + | (Self::Double(_, _), Self::Single(_)) + | (Self::Single(_) | Self::Double(_, _), Self::Double(_, _)) => Self::Multiple, + (Self::NonIdent, _) | (_, Self::NonIdent) => Self::NonIdent, + (Self::Single(il1), Self::Single(il2)) => Self::Double(il1, il2), + } + } +} + +impl AddAssign for IdentDifference { + fn add_assign(&mut self, other: Self) { + *self = *self + other + } +} + +impl IdentDifference { + /// Returns true if learning about more differences will not change the value + /// of this `IdentDifference`, and false otherwise. + fn is_complete(&self) -> bool { + match self { + Self::NoDifference | Self::Single(_) | Self::Double(_, _) => false, + Self::Multiple | Self::NonIdent => true, + } + } +} + +fn ident_difference_expr(left: &Expr, right: &Expr) -> IdentDifference { + ident_difference_expr_with_base_location(left, right, IdentLocation::default()).0 +} + +fn ident_difference_expr_with_base_location( + left: &Expr, + right: &Expr, + mut base: IdentLocation, +) -> (IdentDifference, IdentLocation) { + // Ideally, this function should not use IdentIter because it should return + // early if the expressions have any non-ident differences. We want that early + // return because if without that restriction the lint would lead to false + // positives. + // + // But, we cannot (easily?) use a `rustc_ast::visit::Visitor`, since we need + // the two expressions to be walked in lockstep. And without a `Visitor`, we'd + // have to do all the AST traversal ourselves, which is a lot of work, since to + // do it properly we'd need to be able to handle more or less every possible + // AST node since `Item`s can be written inside `Expr`s. + // + // In practice, it seems likely that expressions, above a certain size, that + // happen to use the exact same idents in the exact same order, and which are + // not structured the same, would be rare. Therefore it seems likely that if + // we do only the first layer of matching ourselves and eventually fallback on + // IdentIter, then the output of this function will be almost always be correct + // in practice. + // + // If it turns out that problematic cases are more prelavent than we assume, + // then we should be able to change this function to do the correct traversal, + // without needing to change the rest of the code. + + #![allow(clippy::enum_glob_use)] + use ExprKind::*; + + match ( + &strip_non_ident_wrappers(left).kind, + &strip_non_ident_wrappers(right).kind, + ) { + (Yield(_), Yield(_)) + | (Try(_), Try(_)) + | (Paren(_), Paren(_)) + | (Repeat(_, _), Repeat(_, _)) + | (Struct(_, _, _), Struct(_, _, _)) + | (MacCall(_), MacCall(_)) + | (LlvmInlineAsm(_), LlvmInlineAsm(_)) + | (InlineAsm(_), InlineAsm(_)) + | (Ret(_), Ret(_)) + | (Continue(_), Continue(_)) + | (Break(_, _), Break(_, _)) + | (AddrOf(_, _, _), AddrOf(_, _, _)) + | (Path(_, _), Path(_, _)) + | (Range(_, _, _), Range(_, _, _)) + | (Index(_, _), Index(_, _)) + | (Field(_, _), Field(_, _)) + | (AssignOp(_, _, _), AssignOp(_, _, _)) + | (Assign(_, _, _), Assign(_, _, _)) + | (TryBlock(_), TryBlock(_)) + | (Await(_), Await(_)) + | (Async(_, _, _), Async(_, _, _)) + | (Block(_, _), Block(_, _)) + | (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _)) + | (Match(_, _), Match(_, _)) + | (Loop(_, _), Loop(_, _)) + | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) + | (While(_, _, _), While(_, _, _)) + | (If(_, _, _), If(_, _, _)) + | (Let(_, _), Let(_, _)) + | (Type(_, _), Type(_, _)) + | (Cast(_, _), Cast(_, _)) + | (Lit(_), Lit(_)) + | (Unary(_, _), Unary(_, _)) + | (Binary(_, _, _), Binary(_, _, _)) + | (Tup(_), Tup(_)) + | (MethodCall(_, _, _), MethodCall(_, _, _)) + | (Call(_, _), Call(_, _)) + | (ConstBlock(_), ConstBlock(_)) + | (Array(_), Array(_)) + | (Box(_), Box(_)) => { + // keep going + }, + _ => { + return (IdentDifference::NonIdent, base); + }, + } + + let mut difference = IdentDifference::NoDifference; + + for (left_attr, right_attr) in left.attrs.iter().zip(right.attrs.iter()) { + let (new_difference, new_base) = + ident_difference_via_ident_iter_with_base_location(left_attr, right_attr, base); + base = new_base; + difference += new_difference; + if difference.is_complete() { + return (difference, base); + } + } + + let (new_difference, new_base) = ident_difference_via_ident_iter_with_base_location(left, right, base); + base = new_base; + difference += new_difference; + + (difference, base) +} + +fn ident_difference_via_ident_iter_with_base_location>( + left: Iterable, + right: Iterable, + mut base: IdentLocation, +) -> (IdentDifference, IdentLocation) { + // See the note in `ident_difference_expr_with_base_location` about `IdentIter` + let mut difference = IdentDifference::NoDifference; + + let mut left_iterator = left.into(); + let mut right_iterator = right.into(); + + loop { + match (left_iterator.next(), right_iterator.next()) { + (Some(left_ident), Some(right_ident)) => { + if !eq_id(left_ident, right_ident) { + difference += IdentDifference::Single(base); + if difference.is_complete() { + return (difference, base); + } + } + }, + (Some(_), None) | (None, Some(_)) => { + return (IdentDifference::NonIdent, base); + }, + (None, None) => { + return (difference, base); + }, + } + base += IdentLocation { index: 1 }; + } +} + +fn get_ident(expr: &Expr, location: IdentLocation) -> Option { + IdentIter::from(expr).nth(location.index) +} + +fn suggestion_with_swapped_ident( + cx: &EarlyContext<'_>, + expr: &Expr, + location: IdentLocation, + new_ident: Ident, + applicability: &mut Applicability, +) -> Option { + get_ident(expr, location).and_then(|current_ident| { + if eq_id(current_ident, new_ident) { + // We never want to suggest a non-change + return None; + } + + Some(format!( + "{}{}{}", + snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), + new_ident.to_string(), + snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), + )) + }) +} + +fn skip_index(iter: Iter, index: usize) -> impl Iterator +where + Iter: Iterator, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index fcf7a4b1367..31b4e25411b 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -10,6 +10,17 @@ use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; use std::mem; +pub mod ident_iter; +pub use ident_iter::IdentIter; + +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { + use BinOpKind::*; + matches!( + kind, + Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr + ) +} + /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) diff --git a/clippy_lints/src/utils/ast_utils/ident_iter.rs b/clippy_lints/src/utils/ast_utils/ident_iter.rs new file mode 100644 index 00000000000..eefcbabd835 --- /dev/null +++ b/clippy_lints/src/utils/ast_utils/ident_iter.rs @@ -0,0 +1,45 @@ +use core::iter::FusedIterator; +use rustc_ast::visit::{walk_attribute, walk_expr, Visitor}; +use rustc_ast::{Attribute, Expr}; +use rustc_span::symbol::Ident; + +pub struct IdentIter(std::vec::IntoIter); + +impl Iterator for IdentIter { + type Item = Ident; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl FusedIterator for IdentIter {} + +impl From<&Expr> for IdentIter { + fn from(expr: &Expr) -> Self { + let mut visitor = IdentCollector::default(); + + walk_expr(&mut visitor, expr); + + IdentIter(visitor.0.into_iter()) + } +} + +impl From<&Attribute> for IdentIter { + fn from(attr: &Attribute) -> Self { + let mut visitor = IdentCollector::default(); + + walk_attribute(&mut visitor, attr); + + IdentIter(visitor.0.into_iter()) + } +} + +#[derive(Default)] +struct IdentCollector(Vec); + +impl Visitor<'_> for IdentCollector { + fn visit_ident(&mut self, ident: Ident) { + self.0.push(ident); + } +} diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 4e09d19ea21..7ab23320db6 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -86,3 +86,12 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn check_nested(n1: &Nested, n2: &Nested) -> bool { + // `n2.inner.0.0` mistyped as `n1.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index ad81b35a766..8ef658af8df 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,13 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:96:5 + | +LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: aborting due to 28 previous errors diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs new file mode 100644 index 00000000000..dd6f4ec7bd9 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.rs @@ -0,0 +1,207 @@ +#![warn(clippy::suspicious_operation_groupings)] + +struct Vec3 { + x: f64, + y: f64, + z: f64, +} + +impl Eq for Vec3 {} + +impl PartialEq for Vec3 { + fn eq(&self, other: &Self) -> bool { + // This should trigger the lint because `self.x` is compared to `other.y` + self.x == other.y && self.y == other.y && self.z == other.z + } +} + +struct S { + a: i32, + b: i32, + c: i32, + d: i32, +} + +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { + // There's no `s1.b` + s1.a < s2.a && s1.a < s2.b +} + +struct SAOnly { + a: i32, +} + +impl S { + fn a(&self) -> i32 { + 0 + } +} + +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SAOnly) -> bool { + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1.a() < s1.b +} + +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool { + macro_rules! s1 { + () => { + S { + a: 1, + b: 1, + c: 1, + d: 1, + } + }; + } + + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1!().a < s1.b +} + +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SAOnly) -> bool { + // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid + s1.a < s2.a && s1.b < s1.b +} + +fn permissable(s1: &S, s2: &S) -> bool { + // Something like this seems like it might actually be what is desired. + s1.a == s2.b +} + +fn non_boolean_operators(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d +} + +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.c + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + s1.a * s2.a + s2.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s1.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 { + // There's no `s2.a` + s1.a * s1.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s1.c +} + +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) { + ( + s1.b * s2.c - s1.c * s2.b, + s1.c * s2.a - s1.a * s2.c, + s1.a * s2.b - s1.b * s2.a, + ) +} + +fn outer_parens_simple(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + (s1.a * s2.a + s1.b * s1.b) +} + +fn outer_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) +} + +fn inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) +} + +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) +} + +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) +} + +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) +} + +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) +} + +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + (s1.a * s2.a + s2.b * s2.b) / 2 +} + +fn inside_function_call(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) +} + +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d +} + +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d +} + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.2.0` + (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 +} + +// `eq_op` should catch this one. +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool { + if strict { + s1.a < s2.a && s1.b < s2.b + } else { + // There's no `s1.b` in this subexpression + s1.a <= s2.a && s1.a <= s2.b + } +} + +fn inside_an_if_statement(s1: &S, s2: &S) { + // There's no `s1.b` + if s1.a < s2.a && s1.a < s2.b { + s1.c = s2.c; + } +} + +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) +} + +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) +} + +fn main() {} diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr new file mode 100644 index 00000000000..ce7108217f1 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -0,0 +1,166 @@ +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:14:9 + | +LL | self.x == other.y && self.y == other.y && self.z == other.z + | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | + = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:14:9 + | +LL | self.x == other.y && self.y == other.y && self.z == other.z + | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:27:20 + | +LL | s1.a < s2.a && s1.a < s2.b + | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:75:33 + | +LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:80:19 + | +LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:80:19 + | +LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:85:19 + | +LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:90:19 + | +LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:95:5 + | +LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.a * s2.a` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:100:33 + | +LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:113:20 + | +LL | (s1.a * s2.a + s1.b * s1.b) + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:118:34 + | +LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:123:38 + | +LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:128:39 + | +LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:133:42 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:133:42 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:138:40 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:143:40 + | +LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:148:20 + | +LL | (s1.a * s2.a + s2.b * s2.b) / 2 + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:153:35 + | +LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:158:29 + | +LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d + | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:163:17 + | +LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d + | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:172:77 + | +LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: I think you meant: `(n1.inner.2).0 == (n2.inner.2).0` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:186:25 + | +LL | s1.a <= s2.a && s1.a <= s2.b + | ^^^^^^^^^^^^ help: I think you meant: `s1.b <= s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:192:23 + | +LL | if s1.a < s2.a && s1.a < s2.b { + | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:199:48 + | +LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) + | ^^^^^^^^^^^^^ help: I think you meant: `-s1.c * -s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:204:27 + | +LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) + | ^^^^^^^^^^^^^ help: I think you meant: `-s1.b < -s2.b` + +error: aborting due to 27 previous errors + From c1b991588f3b2945bddfded808bdab45e250a8dd Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 28 Nov 2020 17:03:20 +0100 Subject: [PATCH 1010/1110] Fix weird dogfood error --- clippy_lints/src/redundant_closure_call.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 49cb2ffc4e3..f398b3fff25 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { cx: &'a LateContext<'tcx>, path: &'tcx hir::Path<'tcx>, count: usize, - }; + } impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type Map = Map<'tcx>; @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } - }; + } let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count From 0e5aee1fc1251493c35a4344700798e9a586ef16 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 28 Nov 2020 17:18:15 +0100 Subject: [PATCH 1011/1110] items_after_statements: don't lint when they a separated by trailing semicolons --- clippy_lints/src/items_after_statements.rs | 4 ++-- tests/ui/item_after_statement.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 8998fae09de..0927d218446 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -58,12 +58,12 @@ impl EarlyLintPass for ItemsAfterStatements { return; } - // skip initial items + // skip initial items and trailing semicolons let stmts = item .stmts .iter() .map(|stmt| &stmt.kind) - .skip_while(|s| matches!(**s, StmtKind::Item(..))); + .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty)); // lint on all further items for stmt in stmts { diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index 377e58e4417..d439ca1e4e1 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -37,3 +37,16 @@ fn mac() { b!(); println!("{}", a); } + +fn semicolon() { + struct S { + a: u32, + }; + impl S { + fn new(a: u32) -> Self { + Self { a } + } + } + + let _ = S::new(3); +} From f7b2098e1c4f8e13ec2194f7f094f471b4056f97 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 29 Nov 2020 01:55:15 +0900 Subject: [PATCH 1012/1110] Fix a false positive in `unnecessary_wraps` --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 2 ++ tests/ui/unnecessary_wraps.rs | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 50dd760432d..004b8416fc1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3878,7 +3878,7 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } // Returns `true` if `expr` contains a return expression -fn contains_return(expr: &hir::Expr<'_>) -> bool { +pub(crate) fn contains_return(expr: &hir::Expr<'_>) -> bool { struct RetCallFinder { found: bool, } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 25ecc7a82f1..7b550c702cd 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,4 @@ +use crate::methods::contains_return; use crate::utils::{ in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, @@ -95,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if let ExprKind::Path(ref qpath) = func.kind; if match_qpath(qpath, path); if args.len() == 1; + if !contains_return(&args[0]); then { suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); true diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index a53dec8f91a..a4570098d71 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -109,6 +109,13 @@ impl B for A { } } +fn issue_6384(s: &str) -> Option<&str> { + Some(match s { + "a" => "A", + _ => return None, + }) +} + fn main() { // method calls are not linted func1(true, true); From 2c26cb14db4d3a86aea0a897f9b727cef4e72e27 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 29 Nov 2020 02:18:05 +0900 Subject: [PATCH 1013/1110] Move `contains_return` to utils/mod.rs --- clippy_lints/src/methods/mod.rs | 43 ++++----------------------- clippy_lints/src/unnecessary_wraps.rs | 3 +- clippy_lints/src/utils/mod.rs | 30 +++++++++++++++++++ 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 004b8416fc1..1476408e0fb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -14,10 +14,8 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; -use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -28,11 +26,12 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, - match_trait_method, match_type, match_var, meets_msrv, method_calls, method_chain_args, paths, remove_blocks, - return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, + contains_return, contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + walk_ptrs_ty_depth, SpanlessEq, }; use semver::{Version, VersionReq}; @@ -3877,36 +3876,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } } -// Returns `true` if `expr` contains a return expression -pub(crate) fn contains_return(expr: &hir::Expr<'_>) -> bool { - struct RetCallFinder { - found: bool, - } - - impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if self.found { - return; - } - if let hir::ExprKind::Ret(..) = &expr.kind { - self.found = true; - } else { - intravisit::walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - } - - let mut visitor = RetCallFinder { found: false }; - visitor.visit_expr(expr); - visitor.found -} - fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if_chain! { if args.len() == 2; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 7b550c702cd..360df2a6752 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,5 @@ -use crate::methods::contains_return; use crate::utils::{ - in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 6f89e51279a..850abc3bae7 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -572,6 +572,36 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { cn.result } +/// Returns `true` if `expr` contains a return expression +pub fn contains_return(expr: &hir::Expr<'_>) -> bool { + struct RetCallFinder { + found: bool, + } + + impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if self.found { + return; + } + if let hir::ExprKind::Ret(..) = &expr.kind { + self.found = true; + } else { + hir::intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + } + + let mut visitor = RetCallFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + /// Converts a span to a code snippet if available, otherwise use default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want From e266708c42dd3d9489586e65b9c6cd1bee0046d5 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Wed, 4 Nov 2020 21:16:25 +0300 Subject: [PATCH 1014/1110] do not trigger MATCH_LIKE_MATCHES_MACRO lint with attrs - it can't be solved completely for attrs evaluated into `false` - change applicability to MaybeIncorrect and mention it in docs --- clippy_lints/src/matches.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d695af4de21..c49abbf781e 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -459,7 +459,8 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** None + /// **Known problems:** It can be FP triggered, when some arms have `cfg` + /// attributes, which evaluate into `false`. /// /// **Example:** /// ```rust @@ -1167,13 +1168,16 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr if b0 != b1; let if_guard = &b0_arms[0].guard; if if_guard.is_none() || b0_arms.len() == 1; + if b0_arms[0].attrs.is_empty(); if b0_arms[1..].iter() .all(|arm| { find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && - arm.guard.is_none() + arm.guard.is_none() && arm.attrs.is_empty() }); then { - let mut applicability = Applicability::MachineApplicable; + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; b0_arms.iter() From 22e7775aa798c1e4688089c150c0a077b9875bf0 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 28 Nov 2020 18:58:53 +0100 Subject: [PATCH 1015/1110] Change formulation of known problems section --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c49abbf781e..52da580b521 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -459,8 +459,8 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** It can be FP triggered, when some arms have `cfg` - /// attributes, which evaluate into `false`. + /// **Known problems:** This lint falsely triggers, if there are arms with + /// `cfg` attributes that remove an arm evaluating to `false`. /// /// **Example:** /// ```rust From 76f2c10fb6afb9071c56e8b6e410671573648ef4 Mon Sep 17 00:00:00 2001 From: Rajkumar Natarajan Date: Sat, 28 Nov 2020 12:19:57 -0500 Subject: [PATCH 1016/1110] issue_6357 update unreachable macro usage --- clippy_lints/src/panic_unimplemented.rs | 9 ++------- tests/ui/panicking_macros.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 31b03ecd101..359620cc079 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { /// ``` pub UNREACHABLE, restriction, - "`unreachable!` should not be present in production code" + "usage of the `unreachable!` macro" } declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); @@ -85,12 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { } else if is_expn_of(expr.span, "todo").is_some() { span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint( - cx, - UNREACHABLE, - span, - "`unreachable` should not be present in production code", - ); + span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); } else if is_expn_of(expr.span, "panic").is_some() { span_lint(cx, PANIC, span, "`panic` should not be present in production code"); } diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 83234c0ed92..6028323a3c8 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -62,7 +62,7 @@ error: `unimplemented` should not be present in production code LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 | LL | unreachable!(); @@ -70,7 +70,7 @@ LL | unreachable!(); | = note: `-D clippy::unreachable` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); @@ -78,7 +78,7 @@ LL | unreachable!("message"); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); @@ -102,7 +102,7 @@ error: `unimplemented` should not be present in production code LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); From 84cdb0a939cec1e13d6f464bb7b036ff3b92dfb0 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 28 Nov 2020 22:40:46 +0100 Subject: [PATCH 1017/1110] Fix formatting --- clippy_lints/src/matches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 52da580b521..665c59d7093 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -460,7 +460,7 @@ declare_clippy_lint! { /// **Why is this bad?** Readability and needless complexity. /// /// **Known problems:** This lint falsely triggers, if there are arms with - /// `cfg` attributes that remove an arm evaluating to `false`. + /// `cfg` attributes that remove an arm evaluating to `false`. /// /// **Example:** /// ```rust From cd087e5c5e65fa61c0562ceb2d2488f8f5660454 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Sat, 28 Nov 2020 19:41:27 +0530 Subject: [PATCH 1018/1110] add rustc-semver to dependencies switch Version/VersionReq usages to RustcVersion --- clippy_lints/Cargo.toml | 1 + clippy_lints/src/manual_non_exhaustive.rs | 14 ++++---------- clippy_lints/src/manual_strip.rs | 14 ++++---------- clippy_lints/src/matches.rs | 14 ++++---------- clippy_lints/src/methods/mod.rs | 16 +++++----------- clippy_lints/src/utils/mod.rs | 10 +++++----- 6 files changed, 23 insertions(+), 46 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..45fd87b169f 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -28,6 +28,7 @@ smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" +rustc-semver="1.0.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 703e6feeca5..91849e74887 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -4,17 +4,11 @@ use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantDat use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use semver::{Version, VersionReq}; -const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version { - major: 1, - minor: 40, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -66,12 +60,12 @@ declare_clippy_lint! { #[derive(Clone)] pub struct ManualNonExhaustive { - msrv: Option, + msrv: Option, } impl ManualNonExhaustive { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv } } } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index e17e3adb94f..f593abdb104 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -13,18 +13,12 @@ use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; -use semver::{Version, VersionReq}; -const MANUAL_STRIP_MSRV: Version = Version { - major: 1, - minor: 45, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); declare_clippy_lint! { /// **What it does:** @@ -61,12 +55,12 @@ declare_clippy_lint! { } pub struct ManualStrip { - msrv: Option, + msrv: Option, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv } } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7d64fa6c262..86e3e2f637e 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -20,10 +20,10 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty, TyS}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::{sym, Symbol}; -use semver::{Version, VersionReq}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -535,13 +535,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { - msrv: Option, + msrv: Option, infallible_destructuring_match_linted: bool, } impl Matches { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv, ..Matches::default() @@ -568,13 +568,7 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); -const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version { - major: 1, - minor: 42, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0); impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1476408e0fb..8002c27a5e9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -18,6 +18,7 @@ use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -33,7 +34,6 @@ use crate::utils::{ snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; -use semver::{Version, VersionReq}; declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. @@ -1405,12 +1405,12 @@ declare_clippy_lint! { } pub struct Methods { - msrv: Option, + msrv: Option, } impl Methods { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv } } } @@ -3470,13 +3470,7 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { ); } -const OPTION_AS_REF_DEREF_MSRV: Version = Version { - major: 1, - minor: 40, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s fn lint_option_as_ref_deref<'tcx>( @@ -3485,7 +3479,7 @@ fn lint_option_as_ref_deref<'tcx>( as_ref_args: &[hir::Expr<'_>], map_args: &[hir::Expr<'_>], is_mut: bool, - msrv: Option<&VersionReq>, + msrv: Option<&RustcVersion>, ) { if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { return; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 850abc3bae7..d68f0698153 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -51,6 +51,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; @@ -59,13 +60,12 @@ use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; -use semver::{Version, VersionReq}; use smallvec::SmallVec; use crate::consts::{constant, Constant}; -pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { - if let Ok(version) = VersionReq::parse(msrv) { +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { @@ -75,8 +75,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt None } -pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { - msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) +pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { + msrv.map_or(true, |msrv| msrv > lint_msrv) } macro_rules! extract_msrv_attr { From 4e4b8319e83d1ec52253dd33c3d108b96fca9024 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Sat, 28 Nov 2020 19:54:28 +0530 Subject: [PATCH 1019/1110] fix msrv in test --- tests/ui/min_rust_version_no_patch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs index 515fe8f95e9..98fffe1e351 100644 --- a/tests/ui/min_rust_version_no_patch.rs +++ b/tests/ui/min_rust_version_no_patch.rs @@ -1,6 +1,6 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "^1.0"] +#![clippy::msrv = "1.0"] fn manual_strip_msrv() { let s = "hello, world!"; From a75ab302d28b6751fa1d1d5e47f8a75cebbaaf79 Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sat, 28 Nov 2020 23:17:43 -0800 Subject: [PATCH 1020/1110] fix dogfood tests --- clippy_lints/src/lib.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..52237b76c35 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -966,22 +966,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - let parsed_msrv = conf.msrv.as_ref().and_then(|s| { + let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); None }) }); - let msrv = parsed_msrv.clone(); - store.register_late_pass(move || box methods::Methods::new(msrv.clone())); - let msrv = parsed_msrv.clone(); - store.register_late_pass(move || box matches::Matches::new(msrv.clone())); - let msrv = parsed_msrv.clone(); - store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone())); - let msrv = parsed_msrv; - store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone())); - + store.register_late_pass(move || box methods::Methods::new(msrv)); + store.register_late_pass(move || box matches::Matches::new(msrv)); + store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); + store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); From af095db64c48caa73211f40e1648dadd6a23a2ca Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sun, 29 Nov 2020 00:33:07 -0800 Subject: [PATCH 1021/1110] fix msrv check --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d68f0698153..9c5e55b1ed5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -76,7 +76,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt } pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv > lint_msrv) + msrv.map_or(true, |msrv| msrv >= lint_msrv) } macro_rules! extract_msrv_attr { From 61b29281e744b6af410e9256cc2e9369a3dc173a Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Sun, 29 Nov 2020 17:08:56 +0530 Subject: [PATCH 1022/1110] add more tests for msrv --- tests/ui/min_rust_version_attr.rs | 38 ++++++++++++++++++++++++++- tests/ui/min_rust_version_attr.stderr | 37 ++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/ui/min_rust_version_attr.stderr diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 8ed483a3ac6..1026cc40d3b 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -35,7 +35,7 @@ fn match_same_arms2() { }; } -fn manual_strip_msrv() { +pub fn manual_strip_msrv() { let s = "hello, world!"; if s.starts_with("hello, ") { assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); @@ -49,3 +49,39 @@ fn main() { match_same_arms2(); manual_strip_msrv(); } + +mod meets_msrv { + #![feature(custom_inner_attributes)] + #![clippy::msrv = "1.45.0"] + + fn main() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } + } +} + +mod just_under_msrv { + #![feature(custom_inner_attributes)] + #![clippy::msrv = "1.46.0"] + + fn main() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } + } +} + +mod just_above_msrv { + #![feature(custom_inner_attributes)] + #![clippy::msrv = "1.44.0"] + + fn main() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } + } +} diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr new file mode 100644 index 00000000000..3e1af046e7a --- /dev/null +++ b/tests/ui/min_rust_version_attr.stderr @@ -0,0 +1,37 @@ +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:60:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:59:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:72:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:71:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: aborting due to 2 previous errors + From 2838b044875b84bb9dc515ba3aa0c1b9772d870b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 8 Nov 2020 13:39:46 +0100 Subject: [PATCH 1023/1110] add internal-lints feature to enable clippys internal lints (off by default) --- .github/workflows/clippy_bors.yml | 4 +- Cargo.toml | 5 +- clippy_lints/Cargo.toml | 2 + clippy_lints/src/lib.rs | 28 ++++++++--- clippy_lints/src/utils/diagnostics.rs | 4 +- clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/paths.rs | 4 ++ tests/compile-test.rs | 15 +++++- tests/dogfood.rs | 47 +++++++++++++------ .../collapsible_span_lint_calls.fixed | 0 .../collapsible_span_lint_calls.rs | 0 .../collapsible_span_lint_calls.stderr | 0 .../{ui => ui-internal}/custom_ice_message.rs | 0 .../custom_ice_message.stderr | 0 tests/{ui => ui-internal}/default_lint.rs | 0 tests/{ui => ui-internal}/default_lint.stderr | 0 tests/{ui => ui-internal}/invalid_paths.rs | 0 .../{ui => ui-internal}/invalid_paths.stderr | 0 .../lint_without_lint_pass.rs | 0 .../lint_without_lint_pass.stderr | 0 .../match_type_on_diag_item.rs | 0 .../match_type_on_diag_item.stderr | 0 .../{ui => ui-internal}/outer_expn_data.fixed | 0 tests/{ui => ui-internal}/outer_expn_data.rs | 0 .../outer_expn_data.stderr | 0 25 files changed, 84 insertions(+), 26 deletions(-) rename tests/{ui => ui-internal}/collapsible_span_lint_calls.fixed (100%) rename tests/{ui => ui-internal}/collapsible_span_lint_calls.rs (100%) rename tests/{ui => ui-internal}/collapsible_span_lint_calls.stderr (100%) rename tests/{ui => ui-internal}/custom_ice_message.rs (100%) rename tests/{ui => ui-internal}/custom_ice_message.stderr (100%) rename tests/{ui => ui-internal}/default_lint.rs (100%) rename tests/{ui => ui-internal}/default_lint.stderr (100%) rename tests/{ui => ui-internal}/invalid_paths.rs (100%) rename tests/{ui => ui-internal}/invalid_paths.stderr (100%) rename tests/{ui => ui-internal}/lint_without_lint_pass.rs (100%) rename tests/{ui => ui-internal}/lint_without_lint_pass.stderr (100%) rename tests/{ui => ui-internal}/match_type_on_diag_item.rs (100%) rename tests/{ui => ui-internal}/match_type_on_diag_item.stderr (100%) rename tests/{ui => ui-internal}/outer_expn_data.fixed (100%) rename tests/{ui => ui-internal}/outer_expn_data.rs (100%) rename tests/{ui => ui-internal}/outer_expn_data.stderr (100%) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 7509d90c6c2..11c1eeac1cf 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -132,10 +132,10 @@ jobs: run: cargo build --features deny-warnings - name: Test - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings --features internal-lints - name: Test clippy_lints - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings --features internal-lints working-directory: clippy_lints - name: Test rustc_tools_util diff --git a/Cargo.toml b/Cargo.toml index 1ddcd18598d..a765390c603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ path = "src/driver.rs" clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update semver = "0.11" -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } tempfile = { version = "3.1.0", optional = true } [dev-dependencies] @@ -49,8 +49,9 @@ derive-new = "0.5" rustc-workspace-hack = "1.0.0" [build-dependencies] -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } [features] deny-warnings = [] integration = ["tempfile"] +internal-lints = ["clippy_lints/internal-lints"] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..969249cc446 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -36,3 +36,5 @@ syn = { version = "1", features = ["full"] } [features] deny-warnings = [] +# build clippy with internal lints enabled, off by default +internal-lints = [] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..a58f7eb3666 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -904,14 +904,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, + #[cfg(feature = "internal-lints")] &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + #[cfg(feature = "internal-lints")] &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::DEFAULT_LINT, + #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + #[cfg(feature = "internal-lints")] &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + #[cfg(feature = "internal-lints")] &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, @@ -932,11 +941,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); - store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); - store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); - store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + #[cfg(feature = "internal-lints")] + { + store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + } store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); @@ -1122,6 +1134,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); + #[cfg(feature = "internal-lints")] store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); @@ -1136,6 +1149,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); + #[cfg(feature = "internal-lints")] store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); @@ -1152,6 +1166,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); + #[cfg(feature = "internal-lints")] store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); @@ -1177,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + #[cfg(feature = "internal-lints")] store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); @@ -1317,7 +1333,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), ]); - + #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 0a58231558e..a7a6b5855b7 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -186,7 +186,9 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[allow(clippy::collapsible_span_lint_calls)] + +#[allow(clippy::unknown_clippy_lints)] +#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 850abc3bae7..63f14c592bd 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -14,6 +14,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod inspector; +#[cfg(feature = "internal-lints")] pub mod internal_lints; pub mod numeric_literal; pub mod paths; diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 61aeabb7ba7..16e6a016c9e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -31,6 +31,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; +#[cfg(feature = "internal-lints")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; @@ -61,8 +62,10 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; +#[cfg(feature = "internal-lints")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; @@ -133,6 +136,7 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0e8f7683103..ec3af94b9ca 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -12,6 +12,9 @@ use std::path::{Path, PathBuf}; mod cargo; +// whether to run internal tests or not +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); + fn host_lib() -> PathBuf { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } @@ -96,6 +99,16 @@ fn run_mode(cfg: &mut compiletest::Config) { compiletest::run_tests(&cfg); } +fn run_internal_tests(cfg: &mut compiletest::Config) { + // only run internal tests with the internal-tests feature + if !RUN_INTERNAL_TESTS { + return; + } + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui-internal"); + compiletest::run_tests(&cfg); +} + fn run_ui_toml(config: &mut compiletest::Config) { fn run_tests(config: &compiletest::Config, mut tests: Vec) -> Result { let mut result = true; @@ -199,7 +212,6 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Some("main.rs") => {}, _ => continue, } - let paths = compiletest::common::TestPaths { file: file_path, base: config.src_base.clone(), @@ -253,4 +265,5 @@ fn compile_test() { run_mode(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); + run_internal_tests(&mut config); } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 48e0478f169..eae25adf839 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -18,20 +18,39 @@ fn dogfood_clippy() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let output = Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::internal"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); + let output = if cfg!(feature = "internal-lints") { + // with internal lints and internal warnings + Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .args(&["--features", "internal-lints"]) + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .args(&["-D", "clippy::internal"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap() + } else { + // without internal lints or warnings + Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap() + }; println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); diff --git a/tests/ui/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed similarity index 100% rename from tests/ui/collapsible_span_lint_calls.fixed rename to tests/ui-internal/collapsible_span_lint_calls.fixed diff --git a/tests/ui/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs similarity index 100% rename from tests/ui/collapsible_span_lint_calls.rs rename to tests/ui-internal/collapsible_span_lint_calls.rs diff --git a/tests/ui/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr similarity index 100% rename from tests/ui/collapsible_span_lint_calls.stderr rename to tests/ui-internal/collapsible_span_lint_calls.stderr diff --git a/tests/ui/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs similarity index 100% rename from tests/ui/custom_ice_message.rs rename to tests/ui-internal/custom_ice_message.rs diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr similarity index 100% rename from tests/ui/custom_ice_message.stderr rename to tests/ui-internal/custom_ice_message.stderr diff --git a/tests/ui/default_lint.rs b/tests/ui-internal/default_lint.rs similarity index 100% rename from tests/ui/default_lint.rs rename to tests/ui-internal/default_lint.rs diff --git a/tests/ui/default_lint.stderr b/tests/ui-internal/default_lint.stderr similarity index 100% rename from tests/ui/default_lint.stderr rename to tests/ui-internal/default_lint.stderr diff --git a/tests/ui/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs similarity index 100% rename from tests/ui/invalid_paths.rs rename to tests/ui-internal/invalid_paths.rs diff --git a/tests/ui/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr similarity index 100% rename from tests/ui/invalid_paths.stderr rename to tests/ui-internal/invalid_paths.stderr diff --git a/tests/ui/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs similarity index 100% rename from tests/ui/lint_without_lint_pass.rs rename to tests/ui-internal/lint_without_lint_pass.rs diff --git a/tests/ui/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr similarity index 100% rename from tests/ui/lint_without_lint_pass.stderr rename to tests/ui-internal/lint_without_lint_pass.stderr diff --git a/tests/ui/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs similarity index 100% rename from tests/ui/match_type_on_diag_item.rs rename to tests/ui-internal/match_type_on_diag_item.rs diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr similarity index 100% rename from tests/ui/match_type_on_diag_item.stderr rename to tests/ui-internal/match_type_on_diag_item.stderr diff --git a/tests/ui/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed similarity index 100% rename from tests/ui/outer_expn_data.fixed rename to tests/ui-internal/outer_expn_data.fixed diff --git a/tests/ui/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs similarity index 100% rename from tests/ui/outer_expn_data.rs rename to tests/ui-internal/outer_expn_data.rs diff --git a/tests/ui/outer_expn_data.stderr b/tests/ui-internal/outer_expn_data.stderr similarity index 100% rename from tests/ui/outer_expn_data.stderr rename to tests/ui-internal/outer_expn_data.stderr From 958e2e20de762fa45f50e41a58c97548f79f8100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 13 Nov 2020 02:12:48 +0100 Subject: [PATCH 1024/1110] fix clippy-dev update_lints --- clippy_dev/src/lib.rs | 30 +++++++++++++++++++++------- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/src/lib.rs | 36 +++++++++++++++++----------------- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 43cb2954b74..1453ac7efa3 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -146,16 +146,32 @@ pub fn gen_deprecated<'a>(lints: impl Iterator) -> Vec } #[must_use] -pub fn gen_register_lint_list<'a>(lints: impl Iterator) -> Vec { - let pre = " store.register_lints(&[".to_string(); - let post = " ]);".to_string(); - let mut inner = lints +pub fn gen_register_lint_list<'a>( + internal_lints: impl Iterator, + usable_lints: impl Iterator, +) -> Vec { + let header = " store.register_lints(&[".to_string(); + let footer = " ]);".to_string(); + let internal_lints = internal_lints + .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .map(|l| { + format!( + " #[cfg(feature = \"internal-lints\")]\n &{}::{},", + l.module, + l.name.to_uppercase() + ) + }) + .collect::>(); + let other_lints = usable_lints + .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) .sorted() .collect::>(); - inner.insert(0, pre); - inner.push(post); - inner + let mut lint_list = vec![header]; + lint_list.extend(internal_lints); + lint_list.extend(other_lints); + lint_list.push(footer); + lint_list } /// Gathers all files in `src/clippy_lints` and gathers all lints inside diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index fcf093f8835..edf6c5f57a4 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -68,7 +68,7 @@ pub fn run(update_mode: UpdateMode) { "end register lints", false, update_mode == UpdateMode::Change, - || gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())), + || gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), ) .changed; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a58f7eb3666..fed7da3ee4f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -498,6 +498,24 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // begin register lints, do not remove this comment, it’s used in `update_lints` store.register_lints(&[ + #[cfg(feature = "internal-lints")] + &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::DEFAULT_LINT, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::PRODUCE_ICE, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -904,24 +922,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, From b25a6df7754ed3de9914f9d03facdfb09728fbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 13 Nov 2020 11:36:07 +0100 Subject: [PATCH 1025/1110] ci: partly clean build artifacts to work around "Found multiple rlibs for crate `clippy_lints`" compiletest error --- .github/workflows/clippy_bors.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 11c1eeac1cf..a8b4925176c 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -131,11 +131,18 @@ jobs: - name: Build run: cargo build --features deny-warnings - - name: Test - run: cargo test --features deny-warnings --features internal-lints + # compiletest would panic due to "Found multiple rlibs for crate `clippy_lints`" + - name: clean rlibs + run: rm -f ./target/debug/deps/libclippy_lints* - - name: Test clippy_lints - run: cargo test --features deny-warnings --features internal-lints + - name: Build with internal lints + run: cargo build --features deny-warnings,internal-lints + + - name: Test with internal lints + run: cargo test --features deny-warnings,internal-lints + + - name: Test clippy_lints with internal lints + run: cargo test --features deny-warnings,internal-lints working-directory: clippy_lints - name: Test rustc_tools_util From 5df286b636e0bf71a665599f099da18a2b90936c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 27 Nov 2020 12:15:05 -0600 Subject: [PATCH 1026/1110] Improve SpanlessEq for blocks --- clippy_lints/src/utils/hir_utils.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index e4ad105c351..d847d22275e 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -81,7 +81,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - match (&left.kind, &right.kind) { + match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, @@ -306,6 +306,32 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } +/// Some simple reductions like `{ return }` => `return` +fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> { + if let ExprKind::Block(block, _) = kind { + match (block.stmts, block.expr) { + // `{}` => `()` + ([], None) => &ExprKind::Tup(&[]), + ([], Some(expr)) => match expr.kind { + // `{ return .. }` => `return ..` + ExprKind::Ret(..) => &expr.kind, + _ => kind, + }, + ([stmt], None) => match stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { + // `{ return ..; }` => `return ..` + ExprKind::Ret(..) => &expr.kind, + _ => kind, + }, + _ => kind, + }, + _ => kind, + } + } else { + kind + } +} + fn swap_binop<'a>( binop: BinOpKind, lhs: &'a Expr<'a>, From 6e1fbfdb8fe9a6b543fa2b0e688f928d2ee354b8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 27 Nov 2020 17:13:19 -0600 Subject: [PATCH 1027/1110] Add LocalUseVisitor --- clippy_lints/src/utils/visitors.rs | 55 +++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/visitors.rs b/clippy_lints/src/utils/visitors.rs index b0837b6c43e..28b3e79d7a6 100644 --- a/clippy_lints/src/utils/visitors.rs +++ b/clippy_lints/src/utils/visitors.rs @@ -1,5 +1,7 @@ use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, QPath, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -123,3 +125,54 @@ where !ret_finder.failed } } + +pub struct LocalUsedVisitor { + pub local_hir_id: HirId, + pub used: bool, +} + +impl LocalUsedVisitor { + pub fn new(local_hir_id: HirId) -> Self { + Self { + local_hir_id, + used: false, + } + } + + fn check(&mut self, t: T, visit: fn(&mut Self, T)) -> bool { + visit(self, t); + std::mem::replace(&mut self.used, false) + } + + pub fn check_arm(&mut self, arm: &Arm<'_>) -> bool { + self.check(arm, Self::visit_arm) + } + + pub fn check_expr(&mut self, expr: &Expr<'_>) -> bool { + self.check(expr, Self::visit_expr) + } + + pub fn check_stmt(&mut self, stmt: &Stmt<'_>) -> bool { + self.check(stmt, Self::visit_stmt) + } +} + +impl<'v> Visitor<'v> for LocalUsedVisitor { + type Map = Map<'v>; + + fn visit_expr(&mut self, expr: &'v Expr<'v>) { + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { + if let Res::Local(id) = path.res { + if id == self.local_hir_id { + self.used = true; + return; + } + } + } + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} From 28dec3b708c3e0d5e45b6c70f054860cbd53d624 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 24 Nov 2020 20:37:07 -0600 Subject: [PATCH 1028/1110] Add collapsible_match lint --- CHANGELOG.md | 1 + clippy_lints/src/collapsible_match.rs | 172 ++++++++++++++++ clippy_lints/src/lib.rs | 5 + tests/ui/collapsible_match.rs | 278 ++++++++++++++++++++++++++ tests/ui/collapsible_match.stderr | 237 ++++++++++++++++++++++ 5 files changed, 693 insertions(+) create mode 100644 clippy_lints/src/collapsible_match.rs create mode 100644 tests/ui/collapsible_match.rs create mode 100644 tests/ui/collapsible_match.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e76a781f13b..e65e7cc639f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1770,6 +1770,7 @@ Released 2018-09-13 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs new file mode 100644 index 00000000000..a34ba2d00a8 --- /dev/null +++ b/clippy_lints/src/collapsible_match.rs @@ -0,0 +1,172 @@ +use crate::utils::visitors::LocalUsedVisitor; +use crate::utils::{span_lint_and_then, SpanlessEq}; +use if_chain::if_chain; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{MultiSpan, Span}; + +declare_clippy_lint! { + /// **What it does:** Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together + /// without adding any branches. + /// + /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only + /// cases where merging would most likely make the code more readable. + /// + /// **Why is this bad?** It is unnecessarily verbose and complex. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(n) => match n { + /// Ok(n) => n, + /// _ => return, + /// } + /// None => return, + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(Ok(n)) => n, + /// _ => return, + /// }; + /// } + /// ``` + pub COLLAPSIBLE_MATCH, + style, + "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." +} + +declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Match(_expr, arms, _source) = expr.kind { + if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) { + for arm in arms { + check_arm(arm, wild_arm, cx); + } + } + } + } +} + +fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) { + if_chain! { + let expr = strip_singleton_blocks(arm.body); + if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; + // the outer arm pattern and the inner match + if expr_in.span.ctxt() == arm.pat.span.ctxt(); + // there must be no more than two arms in the inner match for this lint + if arms_inner.len() == 2; + // no if guards on the inner match + if arms_inner.iter().all(|arm| arm.guard.is_none()); + // match expression must be a local binding + // match { .. } + if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind; + if let Res::Local(binding_id) = path.res; + // one of the branches must be "wild-like" + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); + let (wild_inner_arm, non_wild_inner_arm) = + (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); + if !pat_contains_or(non_wild_inner_arm.pat); + // the binding must come from the pattern of the containing match arm + // .... => match { .. } + if let Some(binding_span) = find_pat_binding(arm.pat, binding_id); + // the "wild-like" branches must be equal + if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); + // the binding must not be used in the if guard + if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard)); + // ...or anywhere in the inner match + if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm)); + then { + span_lint_and_then( + cx, + COLLAPSIBLE_MATCH, + expr.span, + "Unnecessary nested match", + |diag| { + let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); + help_span.push_span_label(binding_span, "Replace this binding".into()); + help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); + diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern."); + }, + ); + } + } +} + +fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { + while let ExprKind::Block(block, _) = expr.kind { + match (block.stmts, block.expr) { + ([stmt], None) => match stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e, + _ => break, + }, + ([], Some(e)) => expr = e, + _ => break, + } + } + expr +} + +/// A "wild-like" pattern is wild ("_") or `None`. +/// For this lint to apply, both the outer and inner match expressions +/// must have "wild-like" branches that can be combined. +fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool { + if arm.guard.is_some() { + return false; + } + match arm.pat.kind { + PatKind::Binding(..) | PatKind::Wild => true, + PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true, + _ => false, + } +} + +fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { + let mut span = None; + pat.walk_short(|p| match &p.kind { + // ignore OR patterns + PatKind::Or(_) => false, + PatKind::Binding(_bm, _, _ident, _) => { + let found = p.hir_id == hir_id; + if found { + span = Some(p.span); + } + !found + }, + _ => true, + }); + span +} + +fn pat_contains_or(pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + let is_or = matches!(p.kind, PatKind::Or(_)); + result |= is_or; + !is_or + }); + result +} + +fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { + if let Some(none_id) = tcx.lang_items().option_none_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res { + if let Some(variant_id) = tcx.parent(id) { + return variant_id == none_id; + } + } + } + false +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..ca190986cdb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -172,6 +172,7 @@ mod cargo_common_metadata; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; +mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; @@ -531,6 +532,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, + &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, @@ -960,6 +962,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box len_zero::LenZero); store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); + store.register_late_pass(|| box collapsible_match::CollapsibleMatch); store.register_late_pass(|| box unicode::Unicode); store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); @@ -1351,6 +1354,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), + LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), @@ -1617,6 +1621,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), + LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&doc::MISSING_SAFETY_DOC), diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs new file mode 100644 index 00000000000..75640b70ff0 --- /dev/null +++ b/tests/ui/collapsible_match.rs @@ -0,0 +1,278 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] + +fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { + // match without block + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // match with block + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // if let, if let + if let Ok(val) = res_opt { + if let Some(n) = val { + take(n); + } + } + + // if let else, if let else + if let Ok(val) = res_opt { + if let Some(n) = val { + take(n); + } else { + return; + } + } else { + return; + } + + // if let, match + if let Ok(val) = res_opt { + match val { + Some(n) => foo(n), + _ => (), + } + } + + // match, if let + match res_opt { + Ok(val) => { + if let Some(n) = val { + take(n); + } + }, + _ => {}, + } + + // if let else, match + if let Ok(val) = res_opt { + match val { + Some(n) => foo(n), + _ => return, + } + } else { + return; + } + + // match, if let else + match res_opt { + Ok(val) => { + if let Some(n) = val { + take(n); + } else { + return; + } + }, + _ => return, + } + + // None in inner match same as outer wild branch + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + None => return, + }, + _ => return, + } + + // None in outer match same as inner wild branch + match opt_opt { + Some(val) => match val { + Some(n) => foo(n), + _ => return, + }, + None => return, + } + + // if guards on outer match + { + match res_opt { + Ok(val) if make() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ if make() => return, + _ => return, + } + } + + // macro + { + macro_rules! mac { + ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { + match $outer { + $pat => match $e { + $inner_pat => $then, + _ => return, + }, + _ => return, + } + }; + } + // Lint this since the patterns are not defined by the macro. + // Allows the lint to work on if_chain! for example. + // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that + // there is still a better way to write this. + mac!(res_opt => Ok(val), val => Some(n), foo(n)); + } +} + +fn negative_cases(res_opt: Result, String>, res_res: Result, String>) { + // no wild pattern in outer match + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + Err(_) => return, + } + + // inner branch is not wild or None + match res_res { + Ok(val) => match val { + Ok(n) => foo(n), + Err(_) => return, + }, + _ => return, + } + + // statement before inner match + match res_opt { + Ok(val) => { + "hi buddy"; + match val { + Some(n) => foo(n), + _ => return, + } + }, + _ => return, + } + + // statement after inner match + match res_opt { + Ok(val) => { + match val { + Some(n) => foo(n), + _ => return, + } + "hi buddy"; + }, + _ => return, + } + + // wild branches do not match + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => { + "sup"; + return; + }, + }, + _ => return, + } + + // binding used in if guard + match res_opt { + Ok(val) if val.is_some() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // binding used in inner match body + match res_opt { + Ok(val) => match val { + Some(_) => take(val), + _ => return, + }, + _ => return, + } + + // if guard on inner match + { + match res_opt { + Ok(val) => match val { + Some(n) if make() => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + _ => make(), + _ if make() => return, + }, + _ => return, + } + } + + // differing macro contexts + { + macro_rules! mac { + ($val:ident) => { + match $val { + Some(n) => foo(n), + _ => return, + } + }; + } + match res_opt { + Ok(val) => mac!(val), + _ => return, + } + } + + // OR pattern + enum E { + A(T), + B(T), + C(T), + }; + match make::>>() { + E::A(val) | E::B(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match make::>>() { + Some(val) => match val { + E::A(val) | E::B(val) => foo(val), + _ => return, + }, + _ => return, + } +} + +fn make() -> T { + unimplemented!() +} + +fn foo(t: T) -> U { + unimplemented!() +} + +fn take(t: T) {} + +fn main() {} diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr new file mode 100644 index 00000000000..a9e4f911914 --- /dev/null +++ b/tests/ui/collapsible_match.stderr @@ -0,0 +1,237 @@ +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:7:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | + = note: `-D clippy::collapsible-match` implied by `-D warnings` +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:7:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:16:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:16:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:25:9 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:24:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:32:9 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } else { +LL | | return; +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:31:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:43:9 + | +LL | / match val { +LL | | Some(n) => foo(n), +LL | | _ => (), +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:42:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | match val { +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:52:13 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:51:12 + | +LL | Ok(val) => { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:61:9 + | +LL | / match val { +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:60:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | match val { +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:72:13 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } else { +LL | | return; +LL | | } + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:71:12 + | +LL | Ok(val) => { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:83:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | None => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:83:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:92:22 + | +LL | Some(val) => match val { + | ______________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:92:14 + | +LL | Some(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:102:34 + | +LL | Ok(val) if make() => match val { + | __________________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:102:16 + | +LL | Ok(val) if make() => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:109:24 + | +LL | Ok(val) => match val { + | ________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:109:16 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:123:29 + | +LL | $pat => match $e { + | _____________________________^ +LL | | $inner_pat => $then, +LL | | _ => return, +LL | | }, + | |_____________________^ +... +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ------------------------------------------------- in this macro invocation + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:135:28 + | +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ^^^ ^^^^^^^ with this pattern + | | + | Replace this binding + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors + From fff5fa65816f482357989f25a58e98688cb7363d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 24 Nov 2020 20:57:09 -0600 Subject: [PATCH 1029/1110] Eat collapsible_match dogfood --- clippy_lints/src/default.rs | 3 +-- clippy_lints/src/if_let_some_result.rs | 3 +-- clippy_lints/src/implicit_return.rs | 3 +-- clippy_lints/src/implicit_saturating_sub.rs | 3 +-- clippy_lints/src/large_const_arrays.rs | 3 +-- clippy_lints/src/large_stack_arrays.rs | 3 +-- clippy_lints/src/loops.rs | 6 ++--- clippy_lints/src/manual_strip.rs | 3 +-- clippy_lints/src/matches.rs | 24 ++++++++----------- .../methods/manual_saturating_arithmetic.rs | 3 +-- clippy_lints/src/needless_bool.rs | 11 +++------ clippy_lints/src/question_mark.rs | 3 +-- clippy_lints/src/strings.rs | 3 +-- clippy_lints/src/trait_bounds.rs | 3 +-- clippy_lints/src/transmuting_null.rs | 3 +-- clippy_lints/src/types.rs | 6 ++--- clippy_lints/src/utils/higher.rs | 3 +-- 17 files changed, 30 insertions(+), 56 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 612c5355338..f69f6f1412a 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -280,8 +280,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; - if let ExprKind::Path(ref qpath) = binding.kind; - if let QPath::Resolved(_, path) = qpath; + if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; if let Some(second_binding_name) = path.segments.last(); if second_binding_name.ident.name == binding_name; then { diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index e0a1f4c5ca4..1194bd7e55e 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -41,8 +41,7 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match - if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let + if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index ed7f3b9293d..03e95c9e27f 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -68,8 +68,7 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let StmtKind::Semi(expr, ..) = &stmt.kind; // make sure it's a break, otherwise we want to skip - if let ExprKind::Break(.., break_expr) = &expr.kind; - if let Some(break_expr) = break_expr; + if let ExprKind::Break(.., Some(break_expr)) = &expr.kind; then { lint(cx, expr.span, break_expr.span, LINT_BREAK); } diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index b57fe8dc426..3a01acd8fdc 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -59,8 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let Some(target) = subtracts_one(cx, e); // Extracting out the variable name - if let ExprKind::Path(ref assign_path) = target.kind; - if let QPath::Resolved(_, ref ares_path) = assign_path; + if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind; then { // Handle symmetric conditions in the if statement diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 025ff86da39..a76595ed089 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -52,8 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if let ItemKind::Const(hir_ty, _) = &item.kind; let ty = hir_ty_to_ty(cx.tcx, hir_ty); if let ty::Array(element_type, cst) = ty.kind(); - if let ConstKind::Value(val) = cst.val; - if let ConstValue::Scalar(element_count) = val; + if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes()); if self.maximum_allowed_size < element_count * element_size; diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 9fd3780e14e..9a448ab1256 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -43,8 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { if_chain! { if let ExprKind::Repeat(_, _) = expr.kind; if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind(); - if let ConstKind::Value(val) = cst.val; - if let ConstValue::Scalar(element_count) = val; + if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes()); if self.maximum_allowed_size < element_count * element_size; diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 143cbea5537..b0de355e242 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1919,8 +1919,7 @@ fn check_for_single_element_loop<'tcx>( if_chain! { if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; - if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind; - if let [arg_expression] = arg_expr_list; + if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); if let ExprKind::Block(ref block, _) = body.kind; @@ -2025,8 +2024,7 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option let node_str = cx.tcx.hir().get(hir_id); if_chain! { if let Node::Binding(pat) = node_str; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if let BindingAnnotation::Mutable = bind_ann; + if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind; then { return Some(hir_id); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index e17e3adb94f..80f3045415e 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -225,8 +225,7 @@ fn find_stripping<'tcx>( if is_ref_str(self.cx, ex); let unref = peel_ref(ex); if let ExprKind::Index(indexed, index) = &unref.kind; - if let Some(range) = higher::range(index); - if let higher::Range { start, end, .. } = range; + if let Some(higher::Range { start, end, .. }) = higher::range(index); if let ExprKind::Path(path) = &indexed.kind; if qpath_res(self.cx, path, ex.hir_id) == self.target; then { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7d64fa6c262..c223462af26 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -652,8 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), pat.span); if !in_macro(pat.span); - if let PatKind::Struct(ref qpath, fields, true) = pat.kind; - if let QPath::Resolved(_, ref path) = qpath; + if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); if let ty::Adt(def, _) = ty.kind(); @@ -962,16 +961,14 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) if let QPath::Resolved(_, p) = path { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } - } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind { - if let QPath::Resolved(_, p) = path { - // Some simple checks for exhaustive patterns. - // There is a room for improvements to detect more cases, - // but it can be more expensive to do so. - let is_pattern_exhaustive = - |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); - if patterns.iter().all(is_pattern_exhaustive) { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); - } + } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind { + // Some simple checks for exhaustive patterns. + // There is a room for improvements to detect more cases, + // but it can be more expensive to do so. + let is_pattern_exhaustive = + |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); + if patterns.iter().all(is_pattern_exhaustive) { + missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } } } @@ -1446,8 +1443,7 @@ fn is_ref_some_arm(arm: &Arm<'_>) -> Option { if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; - if let ExprKind::Path(ref qpath) = args[0].kind; - if let &QPath::Resolved(_, ref path2) = qpath; + if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { return Some(rb) diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 40a62575861..44c974b9d98 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -90,8 +90,7 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option return Some(MinMax::Max), diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index a799a644e97..42f97b2ac49 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -6,7 +6,6 @@ use crate::utils::sugg::Sugg; use crate::utils::{ higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, }; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; @@ -198,13 +197,9 @@ struct ExpressionInfoWithSpan { } fn is_unary_not(e: &Expr<'_>) -> (bool, Span) { - if_chain! { - if let ExprKind::Unary(unop, operand) = e.kind; - if let UnOp::UnNot = unop; - then { - return (true, operand.span); - } - }; + if let ExprKind::Unary(UnOp::UnNot, operand) = e.kind { + return (true, operand.span); + } (false, e.span) } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d9b280b7a85..b91233ac582 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -176,8 +176,7 @@ impl QuestionMark { if block.stmts.len() == 1; if let Some(expr) = block.stmts.iter().last(); if let StmtKind::Semi(ref expr) = expr.kind; - if let ExprKind::Ret(ret_expr) = expr.kind; - if let Some(ret_expr) = ret_expr; + if let ExprKind::Ret(Some(ret_expr)) = expr.kind; then { return Some(ret_expr); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 42c45be3b45..77e79073378 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -222,8 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if method_names[0] == sym!(as_bytes); // Check for slicer - if let ExprKind::Struct(ref path, _, _) = right.kind; - if let QPath::LangItem(LangItem::Range, _) = path; + if let ExprKind::Struct(QPath::LangItem(LangItem::Range, _), _, _) = right.kind; then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index d4acf8df46d..daff5f81e8c 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -168,8 +168,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !in_macro(bound_predicate.span); - if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind; - if let QPath::Resolved(_, Path { ref segments, .. }) = path; + if let TyKind::Path(QPath::Resolved(_, Path { ref segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index d60306336c6..6b171a0fa1a 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -48,8 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { if_chain! { if let ExprKind::Path(ref _qpath) = args[0].kind; let x = const_eval_context.expr(&args[0]); - if let Some(constant) = x; - if let Constant::RawPtr(0) = constant; + if let Some(Constant::RawPtr(0)) = x; then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c8bdc5a71e6..74ba53e6a9a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -738,8 +738,7 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { if_chain! { if let Some(did) = qpath_res(cx, qpath, id).opt_def_id(); - if let Some(node) = cx.tcx.hir().get_if_local(did); - if let Node::GenericParam(generic_param) = node; + if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; if synthetic == Some(SyntheticTyParamKind::ImplTrait); then { @@ -1470,8 +1469,7 @@ fn check_loss_of_sign(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast // don't lint for positive constants let const_val = constant(cx, &cx.typeck_results(), op); if_chain! { - if let Some((const_val, _)) = const_val; - if let Constant::Int(n) = const_val; + if let Some((Constant::Int(n), _)) = const_val; if let ty::Int(ity) = *cast_from.kind(); if sext(cx.tcx, n, ity) >= 0; then { diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 6d7c5058b4f..01ffac5b559 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -162,8 +162,7 @@ pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr< if let hir::Block { expr: Some(expr), .. } = &**block; if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind; if let hir::ExprKind::DropTemps(cond) = &cond.kind; - if let [arm, ..] = &arms[..]; - if let hir::Arm { body, .. } = arm; + if let [hir::Arm { body, .. }, ..] = &arms[..]; then { return Some((cond, body)); } From a5d6855333c55636bc0fc56efcc83eac7c57ffa7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 28 Nov 2020 20:41:29 -0600 Subject: [PATCH 1030/1110] Use LocalUsedVisitor in more places --- clippy_lints/src/let_if_seq.rs | 53 ++++------------------------------ clippy_lints/src/loops.rs | 29 ++----------------- 2 files changed, 8 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 8243b0a29bc..0d2d95324c4 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,12 +1,11 @@ +use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{higher, qpath_res, snippet, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::intravisit; use rustc_hir::BindingAnnotation; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -66,10 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; if let hir::StmtKind::Expr(ref if_) = expr.kind; if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_); - if !used_in_expr(cx, canonical_id, cond); + if !LocalUsedVisitor::new(canonical_id).check_expr(cond); if let hir::ExprKind::Block(ref then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); - if !used_in_expr(cx, canonical_id, value); + if !LocalUsedVisitor::new(canonical_id).check_expr(value); then { let span = stmt.span.to(if_.span); @@ -136,32 +135,6 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { } } -struct UsedVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - id: hir::HirId, - used: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Path(ref qpath) = expr.kind; - if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id); - if self.id == local_id; - then { - self.used = true; - return; - } - } - intravisit::walk_expr(self, expr); - } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - fn check_assign<'tcx>( cx: &LateContext<'tcx>, decl: hir::HirId, @@ -176,18 +149,10 @@ fn check_assign<'tcx>( if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id); if decl == local_id; then { - let mut v = UsedVisitor { - cx, - id: decl, - used: false, - }; + let mut v = LocalUsedVisitor::new(decl); - for s in block.stmts.iter().take(block.stmts.len()-1) { - intravisit::walk_stmt(&mut v, s); - - if v.used { - return None; - } + if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) { + return None; } return Some(value); @@ -196,9 +161,3 @@ fn check_assign<'tcx>( None } - -fn used_in_expr<'tcx>(cx: &LateContext<'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool { - let mut v = UsedVisitor { cx, id, used: false }; - intravisit::walk_expr(&mut v, expr); - v.used -} diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index b0de355e242..400148ab81d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2,6 +2,7 @@ use crate::consts::constant; use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; +use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, @@ -2069,28 +2070,6 @@ fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { } } -struct LocalUsedVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - local: HirId, - used: bool, -} - -impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if same_var(self.cx, expr, self.local) { - self.used = true; - } else { - walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - struct VarVisitor<'a, 'tcx> { /// context reference cx: &'a LateContext<'tcx>, @@ -2124,11 +2103,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { then { let index_used_directly = same_var(self.cx, idx, self.var); let indexed_indirectly = { - let mut used_visitor = LocalUsedVisitor { - cx: self.cx, - local: self.var, - used: false, - }; + let mut used_visitor = LocalUsedVisitor::new(self.var); walk_expr(&mut used_visitor, idx); used_visitor.used }; From 252083f7e02a3a9174bb39821fd20356ada3dd4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 16 Nov 2020 12:44:05 +0100 Subject: [PATCH 1031/1110] address review comments and rebase ci: always build with internal lints group up internal lints in lib.rs dogfood: we already pass --all-features, no need to enable internal-lints again --- .github/workflows/clippy_bors.yml | 7 ---- clippy_dev/src/lib.rs | 6 ++-- clippy_lints/src/lib.rs | 29 ++++++++--------- tests/dogfood.rs | 53 ++++++++++++------------------- 4 files changed, 35 insertions(+), 60 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index a8b4925176c..784463fe0df 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -128,13 +128,6 @@ jobs: SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - - name: Build - run: cargo build --features deny-warnings - - # compiletest would panic due to "Found multiple rlibs for crate `clippy_lints`" - - name: clean rlibs - run: rm -f ./target/debug/deps/libclippy_lints* - - name: Build with internal lints run: cargo build --features deny-warnings,internal-lints diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 1453ac7efa3..f51c45e9eb5 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -160,13 +160,11 @@ pub fn gen_register_lint_list<'a>( l.module, l.name.to_uppercase() ) - }) - .collect::>(); + }); let other_lints = usable_lints .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) - .sorted() - .collect::>(); + .sorted(); let mut lint_list = vec![header]; lint_list.extend(internal_lints); lint_list.extend(other_lints); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fed7da3ee4f..8fbd44528b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -939,17 +939,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeAPI); + + // all the internal lints #[cfg(feature = "internal-lints")] { - store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); - store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); - store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); + store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); + store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } store.register_late_pass(|| box utils::author::Author); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); + store.register_late_pass(|| box serde_api::SerdeAPI); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); @@ -1134,8 +1140,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); - #[cfg(feature = "internal-lints")] - store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); @@ -1149,8 +1153,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); - #[cfg(feature = "internal-lints")] - store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); @@ -1166,8 +1168,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); - #[cfg(feature = "internal-lints")] - store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); @@ -1175,7 +1175,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1192,8 +1191,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - #[cfg(feature = "internal-lints")] - store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); @@ -1202,7 +1199,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); - store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), @@ -1333,6 +1329,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), ]); + #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), diff --git a/tests/dogfood.rs b/tests/dogfood.rs index eae25adf839..a6163a83d76 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -18,39 +18,26 @@ fn dogfood_clippy() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let output = if cfg!(feature = "internal-lints") { - // with internal lints and internal warnings - Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .args(&["--features", "internal-lints"]) - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .args(&["-D", "clippy::internal"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap() - } else { - // without internal lints or warnings - Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap() - }; + let mut command = Command::new(&*CLIPPY_PATH); + command + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + + // internal lints only exist if we build with the internal-lints feature + if cfg!(feature = "internal-lints") { + command.args(&["-D", "clippy::internal"]); + } + + let output = command.output().unwrap(); + println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); From f059febe85f49fd7b432a24542321f9b948a49de Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 13 Nov 2020 12:13:50 -0600 Subject: [PATCH 1032/1110] Add redundant else lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/redundant_else.rs | 135 +++++++++++++++++++++++++ tests/ui/redundant_else.rs | 154 +++++++++++++++++++++++++++++ tests/ui/redundant_else.stderr | 80 +++++++++++++++ 5 files changed, 374 insertions(+) create mode 100644 clippy_lints/src/redundant_else.rs create mode 100644 tests/ui/redundant_else.rs create mode 100644 tests/ui/redundant_else.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e76a781f13b..c3351793c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2023,6 +2023,7 @@ Released 2018-09-13 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..66895a866ee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod question_mark; mod ranges; mod redundant_clone; mod redundant_closure_call; +mod redundant_else; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -810,6 +811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_closure_call::REDUNDANT_CLOSURE_CALL, + &redundant_else::REDUNDANT_ELSE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -1113,6 +1115,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); @@ -1294,6 +1297,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), + LintId::of(&redundant_else::REDUNDANT_ELSE), LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs new file mode 100644 index 00000000000..3d585cd27a3 --- /dev/null +++ b/clippy_lints/src/redundant_else.rs @@ -0,0 +1,135 @@ +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `else` blocks that can be removed without changing semantics. + /// + /// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity. + /// + /// **Known problems:** Some may prefer to keep the `else` block for clarity. + /// + /// **Example:** + /// + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } else { + /// print!("Moving on..."); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } + /// print!("Moving on..."); + /// } + /// ``` + pub REDUNDANT_ELSE, + pedantic, + "`else` branch that can be removed without changing semantics" +} + +declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]); + +impl EarlyLintPass for RedundantElse { + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) { + if in_external_macro(cx.sess, stmt.span) { + return; + } + // Only look at expressions that are a whole statement + let expr: &Expr = match &stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + _ => return, + }; + // if else + let (mut then, mut els): (&Block, &Expr) = match &expr.kind { + ExprKind::If(_, then, Some(els)) => (then, els), + _ => return, + }; + loop { + if !BreakVisitor::default().check_block(then) { + // then block does not always break + return; + } + match &els.kind { + // else if else + ExprKind::If(_, next_then, Some(next_els)) => { + then = next_then; + els = next_els; + continue; + }, + // else if without else + ExprKind::If(..) => return, + // done + _ => break, + } + } + span_lint_and_help( + cx, + REDUNDANT_ELSE, + els.span, + "redundant else block", + None, + "remove the `else` block and move the contents out", + ); + } +} + +/// Call `check` functions to check if an expression always breaks control flow +#[derive(Default)] +struct BreakVisitor { + is_break: bool, +} + +impl<'ast> Visitor<'ast> for BreakVisitor { + fn visit_block(&mut self, block: &'ast Block) { + self.is_break = match block.stmts.as_slice() { + [.., last] => self.check_stmt(last), + _ => false, + }; + } + + fn visit_expr(&mut self, expr: &'ast Expr) { + self.is_break = match expr.kind { + ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true, + ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)), + ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els), + ExprKind::If(_, _, None) + // ignore loops for simplicity + | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false, + _ => { + walk_expr(self, expr); + return; + }, + }; + } +} + +impl BreakVisitor { + fn check(&mut self, item: T, visit: fn(&mut Self, T)) -> bool { + visit(self, item); + std::mem::replace(&mut self.is_break, false) + } + + fn check_block(&mut self, block: &Block) -> bool { + self.check(block, Self::visit_block) + } + + fn check_expr(&mut self, expr: &Expr) -> bool { + self.check(expr, Self::visit_expr) + } + + fn check_stmt(&mut self, stmt: &Stmt) -> bool { + self.check(stmt, Self::visit_stmt) + } +} diff --git a/tests/ui/redundant_else.rs b/tests/ui/redundant_else.rs new file mode 100644 index 00000000000..737c8a9f8db --- /dev/null +++ b/tests/ui/redundant_else.rs @@ -0,0 +1,154 @@ +#![warn(clippy::redundant_else)] +#![allow(clippy::needless_return)] + +fn main() { + loop { + // break + if foo() { + println!("Love your neighbor;"); + break; + } else { + println!("yet don't pull down your hedge."); + } + // continue + if foo() { + println!("He that lies down with Dogs,"); + continue; + } else { + println!("shall rise up with fleas."); + } + // match block + if foo() { + match foo() { + 1 => break, + _ => return, + } + } else { + println!("You may delay, but time will not."); + } + } + // else if + if foo() { + return; + } else if foo() { + return; + } else { + println!("A fat kitchen makes a lean will."); + } + // let binding outside of block + let _ = { + if foo() { + return; + } else { + 1 + } + }; + // else if with let binding outside of block + let _ = { + if foo() { + return; + } else if foo() { + return; + } else { + 2 + } + }; + // inside if let + let _ = if let Some(1) = foo() { + let _ = 1; + if foo() { + return; + } else { + 1 + } + } else { + 1 + }; + + // + // non-lint cases + // + + // sanity check + if foo() { + let _ = 1; + } else { + println!("Who is wise? He that learns from every one."); + } + // else if without else + if foo() { + return; + } else if foo() { + foo() + }; + // nested if return + if foo() { + if foo() { + return; + } + } else { + foo() + }; + // match with non-breaking branch + if foo() { + match foo() { + 1 => foo(), + _ => return, + } + } else { + println!("Three may keep a secret, if two of them are dead."); + } + // let binding + let _ = if foo() { + return; + } else { + 1 + }; + // assign + let a; + a = if foo() { + return; + } else { + 1 + }; + // assign-op + a += if foo() { + return; + } else { + 1 + }; + // if return else if else + if foo() { + return; + } else if foo() { + 1 + } else { + 2 + }; + // if else if return else + if foo() { + 1 + } else if foo() { + return; + } else { + 2 + }; + // else if with let binding + let _ = if foo() { + return; + } else if foo() { + return; + } else { + 2 + }; + // inside function call + Box::new(if foo() { + return; + } else { + 1 + }); +} + +fn foo() -> T { + unimplemented!("I'm not Santa Claus") +} diff --git a/tests/ui/redundant_else.stderr b/tests/ui/redundant_else.stderr new file mode 100644 index 00000000000..9000cdc814b --- /dev/null +++ b/tests/ui/redundant_else.stderr @@ -0,0 +1,80 @@ +error: redundant else block + --> $DIR/redundant_else.rs:10:16 + | +LL | } else { + | ________________^ +LL | | println!("yet don't pull down your hedge."); +LL | | } + | |_________^ + | + = note: `-D clippy::redundant-else` implied by `-D warnings` + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:17:16 + | +LL | } else { + | ________________^ +LL | | println!("shall rise up with fleas."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:26:16 + | +LL | } else { + | ________________^ +LL | | println!("You may delay, but time will not."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:35:12 + | +LL | } else { + | ____________^ +LL | | println!("A fat kitchen makes a lean will."); +LL | | } + | |_____^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:42:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:52:16 + | +LL | } else { + | ________________^ +LL | | 2 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:61:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: aborting due to 7 previous errors + From 70f6a2cae22cc1245ee62ca493f3027a76b3a381 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 13 Nov 2020 12:46:37 -0600 Subject: [PATCH 1033/1110] Eat redundant else dogfood --- clippy_lints/src/functions.rs | 13 +++++-------- clippy_lints/src/len_zero.rs | 3 +-- clippy_lints/src/matches.rs | 5 ++--- clippy_lints/src/methods/unnecessary_filter_map.rs | 5 ++--- clippy_lints/src/non_expressive_names.rs | 7 +++---- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 8b58d1f2601..fd93548b55c 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -405,13 +405,10 @@ impl<'tcx> Functions { break; } if in_comment { - match line.find("*/") { - Some(i) => { - line = &line[i + 2..]; - in_comment = false; - continue; - }, - None => break, + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; } } else { let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); @@ -423,8 +420,8 @@ impl<'tcx> Functions { in_comment = true; continue; } - break; } + break; } if code_in_line { line_count += 1; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8842901d90b..6fe53351090 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -222,9 +222,8 @@ fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplIte let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) { if cx.access_levels.is_exported(is_empty.id.hir_id) { return; - } else { - "a private" } + "a private" } else { "no corresponding" }; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7d64fa6c262..5c3901e5b28 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -696,10 +696,9 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { // single statement/expr "else" block, don't lint return; - } else { - // block with 2+ statements or 1 expr and 1+ statement - Some(els) } + // block with 2+ statements or 1 expr and 1+ statement + Some(els) } else { // not a block, don't lint return; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 75e123eb593..d082a88cd2d 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -69,10 +69,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } } return (true, false); - } else { - // We don't know. It might do anything. - return (true, true); } + // We don't know. It might do anything. + return (true, true); } } (true, true) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 5b42b61fcde..446426b3e61 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -409,11 +409,10 @@ fn levenstein_not_1(a_name: &str, b_name: &str) -> bool { if let Some(b2) = b_chars.next() { // check if there's just one character inserted return a != b2 || a_chars.ne(b_chars); - } else { - // tuple - // ntuple - return true; } + // tuple + // ntuple + return true; } // for item in items true From 0e207888391fb8b55fa75d19259812b6cb97a75c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 29 Nov 2020 18:21:21 -0600 Subject: [PATCH 1034/1110] Split tests --- tests/ui/collapsible_match.rs | 39 ------------------- tests/ui/collapsible_match.stderr | 60 +---------------------------- tests/ui/collapsible_match2.rs | 53 ++++++++++++++++++++++++++ tests/ui/collapsible_match2.stderr | 61 ++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 98 deletions(-) create mode 100644 tests/ui/collapsible_match2.rs create mode 100644 tests/ui/collapsible_match2.stderr diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 75640b70ff0..a83e6c77b12 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -95,45 +95,6 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> }, None => return, } - - // if guards on outer match - { - match res_opt { - Ok(val) if make() => match val { - Some(n) => foo(n), - _ => return, - }, - _ => return, - } - match res_opt { - Ok(val) => match val { - Some(n) => foo(n), - _ => return, - }, - _ if make() => return, - _ => return, - } - } - - // macro - { - macro_rules! mac { - ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { - match $outer { - $pat => match $e { - $inner_pat => $then, - _ => return, - }, - _ => return, - } - }; - } - // Lint this since the patterns are not defined by the macro. - // Allows the lint to work on if_chain! for example. - // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that - // there is still a better way to write this. - mac!(res_opt => Ok(val), val => Some(n), foo(n)); - } } fn negative_cases(res_opt: Result, String>, res_res: Result, String>) { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index a9e4f911914..63ac6a1613d 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,63 +175,5 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match - --> $DIR/collapsible_match.rs:102:34 - | -LL | Ok(val) if make() => match val { - | __________________________________^ -LL | | Some(n) => foo(n), -LL | | _ => return, -LL | | }, - | |_____________^ - | -help: The outer pattern can be modified to include the inner pattern. - --> $DIR/collapsible_match.rs:102:16 - | -LL | Ok(val) if make() => match val { - | ^^^ Replace this binding -LL | Some(n) => foo(n), - | ^^^^^^^ with this pattern - -error: Unnecessary nested match - --> $DIR/collapsible_match.rs:109:24 - | -LL | Ok(val) => match val { - | ________________________^ -LL | | Some(n) => foo(n), -LL | | _ => return, -LL | | }, - | |_____________^ - | -help: The outer pattern can be modified to include the inner pattern. - --> $DIR/collapsible_match.rs:109:16 - | -LL | Ok(val) => match val { - | ^^^ Replace this binding -LL | Some(n) => foo(n), - | ^^^^^^^ with this pattern - -error: Unnecessary nested match - --> $DIR/collapsible_match.rs:123:29 - | -LL | $pat => match $e { - | _____________________________^ -LL | | $inner_pat => $then, -LL | | _ => return, -LL | | }, - | |_____________________^ -... -LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); - | ------------------------------------------------- in this macro invocation - | -help: The outer pattern can be modified to include the inner pattern. - --> $DIR/collapsible_match.rs:135:28 - | -LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); - | ^^^ ^^^^^^^ with this pattern - | | - | Replace this binding - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 13 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs new file mode 100644 index 00000000000..d571ac4ab69 --- /dev/null +++ b/tests/ui/collapsible_match2.rs @@ -0,0 +1,53 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] + +fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { + // if guards on outer match + { + match res_opt { + Ok(val) if make() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ if make() => return, + _ => return, + } + } + + // macro + { + macro_rules! mac { + ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { + match $outer { + $pat => match $e { + $inner_pat => $then, + _ => return, + }, + _ => return, + } + }; + } + // Lint this since the patterns are not defined by the macro. + // Allows the lint to work on if_chain! for example. + // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that + // there is still a better way to write this. + mac!(res_opt => Ok(val), val => Some(n), foo(n)); + } +} + +fn make() -> T { + unimplemented!() +} + +fn foo(t: T) -> U { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr new file mode 100644 index 00000000000..490d82d12cd --- /dev/null +++ b/tests/ui/collapsible_match2.stderr @@ -0,0 +1,61 @@ +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:8:34 + | +LL | Ok(val) if make() => match val { + | __________________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | + = note: `-D clippy::collapsible-match` implied by `-D warnings` +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:8:16 + | +LL | Ok(val) if make() => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:15:24 + | +LL | Ok(val) => match val { + | ________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:15:16 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:29:29 + | +LL | $pat => match $e { + | _____________________________^ +LL | | $inner_pat => $then, +LL | | _ => return, +LL | | }, + | |_____________________^ +... +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ------------------------------------------------- in this macro invocation + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:41:28 + | +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ^^^ ^^^^^^^ with this pattern + | | + | Replace this binding + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + From e7258ac714941ed5534ae31cddac7164ce1f50c1 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Mon, 30 Nov 2020 11:24:10 +0530 Subject: [PATCH 1035/1110] bump rustc-semver version. use in built method to compare versions --- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 45fd87b169f..9b8ff266f5c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -28,7 +28,7 @@ smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" -rustc-semver="1.0.0" +rustc-semver="1.1.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 9c5e55b1ed5..b2f16007e35 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -76,7 +76,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt } pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv >= lint_msrv) + msrv.map_or(true, |msrv| msrv.meets(*lint_msrv)) } macro_rules! extract_msrv_attr { From 292a54eede3da9fc9b8f8148546ea20ae76755a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 1 Dec 2020 01:26:02 +0100 Subject: [PATCH 1036/1110] CONTRIBUTING: update bors queue url from buildbot2.rlo to bors.rlo --- CONTRIBUTING.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8e2123656e..f8c26e2d456 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,11 +14,16 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Contributing to Clippy](#contributing-to-clippy) - [Getting started](#getting-started) + - [High level approach](#high-level-approach) - [Finding something to fix/improve](#finding-something-to-fiximprove) - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) - [How Clippy works](#how-clippy-works) - [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust) + - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) + - [Performing the sync](#performing-the-sync) + - [Syncing back changes in Clippy to [`rust-lang/rust`]](#syncing-back-changes-in-clippy-to-rust-langrust) + - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) @@ -320,8 +325,8 @@ commands [here][homu_instructions]. [l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash [l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug [homu]: https://github.com/rust-lang/homu -[homu_instructions]: https://buildbot2.rust-lang.org/homu/ -[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy +[homu_instructions]: https://bors.rust-lang.org/ +[homu_queue]: https://bors.rust-lang.org/queue/clippy ## Contributions From 8135ab8a22f4aa6ad071574798397aa131377ca2 Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 16:11:52 -0500 Subject: [PATCH 1037/1110] Moved map_err_ignore to restriction and updated help message --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 5298e16a04d..324a11f140a 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -99,7 +99,7 @@ declare_clippy_lint! { /// } /// ``` pub MAP_ERR_IGNORE, - pedantic, + restriction, "`map_err` should not ignore the original error" } @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { body_span, "`map_err(|_|...` ignores the original error", None, - "Consider wrapping the error in an enum variant", + "Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error", ); } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 390d7ce2e4e..8193f7cfb8e 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider wrapping the error in an enum variant + = help: Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error error: aborting due to previous error From f633ef6bba70b91f0415316fe10bb1f9c016ba33 Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 17:22:03 -0500 Subject: [PATCH 1038/1110] didn't update lint correctly --- clippy_lints/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 167e5b6b87f..013406347f2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1214,6 +1214,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&integer_division::INTEGER_DIVISION), LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(&mem_forget::MEM_FORGET), @@ -1280,7 +1281,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), LintId::of(&manual_ok_or::MANUAL_OK_OR), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_SAME_ARMS), From 75482f917d7fc65190274bd577dcc2e30ba34aff Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 17:44:50 -0500 Subject: [PATCH 1039/1110] Apply suggestions from code review updated help message for the user Co-authored-by: Jane Lusby --- clippy_lints/src/map_err_ignore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 324a11f140a..f3c0515b9bc 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -133,9 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { cx, MAP_ERR_IGNORE, body_span, - "`map_err(|_|...` ignores the original error", + "`map_err(|_|...` wildcard pattern discards the original error", None, - "Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error", + "Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", ); } } From 4bc33d37225db3dd3ab68bb53e855fcf794047db Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 17:49:27 -0500 Subject: [PATCH 1040/1110] Update the stderr message in ui tests --- tests/ui/map_err.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8193f7cfb8e..8ee2941790d 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,11 +1,11 @@ -error: `map_err(|_|...` ignores the original error +error: `map_err(|_|...` wildcard pattern discards the original error --> $DIR/map_err.rs:23:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error + = help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) error: aborting due to previous error From b6113066429bc3108f62b920ccbfc79accfef2dd Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 27 Nov 2020 22:44:02 -0300 Subject: [PATCH 1041/1110] Add lint unsafe_sizeof_count_copies --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + .../src/unsafe_sizeof_count_copies.rs | 98 +++++++++++++ clippy_lints/src/utils/paths.rs | 4 + tests/ui/unsafe_sizeof_count_copies.rs | 54 ++++++++ tests/ui/unsafe_sizeof_count_copies.stderr | 131 ++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 clippy_lints/src/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/unsafe_sizeof_count_copies.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e65e7cc639f..e0f3b82ad25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2124,6 +2124,7 @@ Released 2018-09-13 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name +[`unsafe_sizeof_count_copies`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_sizeof_count_copies [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 167e5b6b87f..1bce0130b40 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -329,6 +329,7 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unsafe_sizeof_count_copies; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + &unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -998,6 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_late_pass(|| box unsafe_sizeof_count_copies::UnsafeSizeofCountCopies); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -1605,6 +1608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1883,6 +1887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs new file mode 100644 index 00000000000..2422df8feba --- /dev/null +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -0,0 +1,98 @@ +//! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee +//! count + +use crate::utils::{match_def_path, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Ty as TyM, TyS}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects expressions where + /// size_of:: is used as the count argument to unsafe + /// memory copying functions like ptr::copy and + /// ptr::copy_nonoverlapping where T is the pointee type + /// of the pointers used + /// + /// **Why is this bad?** These functions expect a count + /// of T and not a number of bytes, which can lead to + /// copying the incorrect amount of bytes, which can + /// result in Undefined Behaviour + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,no_run + /// # use std::ptr::copy_nonoverlapping; + /// # use std::mem::size_of; + /// + /// const SIZE: usize = 128; + /// let x = [2u8; SIZE]; + /// let mut y = [2u8; SIZE]; + /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + /// ``` + pub UNSAFE_SIZEOF_COUNT_COPIES, + correctness, + "unsafe memory copying using a byte count instead of a count of T" +} + +declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); + +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + match &expr.kind { + ExprKind::Call(ref count_func, _func_args) => { + if_chain! { + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) + || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next() + } else { + None + } + } + }, + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { + get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right)) + }, + _ => None, + } +} + +impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) + || match_def_path(cx, def_id, &paths::COPY); + + // Get the pointee type + let _substs = cx.typeck_results().node_substs(func.hir_id); + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + + // Find a size_of call in the count parameter expression and + // check that it's the same type + if let [_src, _dest, count] = &**func_args; + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count); + if TyS::same_type(pointee_ty, ty_used_for_size_of); + then { + span_lint_and_help( + cx, + UNSAFE_SIZEOF_COUNT_COPIES, + expr.span, + "unsafe memory copying using a byte count (Multiplied by size_of::) \ + instead of a count of T", + None, + "use a count of elements instead of a count of bytes for the count parameter, \ + it already gets multiplied by the size of the pointed to type" + ); + } + }; + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 16e6a016c9e..fe763d4bfbb 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -20,6 +20,8 @@ pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; +pub const COPY: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; +pub const COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; @@ -73,6 +75,8 @@ pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "Manua pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; +pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"]; +pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OPS_MODULE: [&str; 2] = ["core", "ops"]; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs new file mode 100644 index 00000000000..0077ed07fce --- /dev/null +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -0,0 +1,54 @@ +#![warn(clippy::unsafe_sizeof_count_copies)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{copy, copy_nonoverlapping}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count is size_of (Should trigger the lint) + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + // Count expression involving multiplication of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + + // Count expression involving nested multiplications of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; + + // Count expression involving divisions of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; + + // No size_of calls (Should not trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + // Different types for pointee and size_of (Should not trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; +} diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr new file mode 100644 index 00000000000..6804df8cdfc --- /dev/null +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -0,0 +1,131 @@ +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:14:14 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:15:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:17:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:21:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:24:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:25:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:29:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:31:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:35:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:38:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:39:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: aborting due to 16 previous errors + From 0f954babef41b16e26b900d3858ceac4005a4506 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 01:47:32 -0300 Subject: [PATCH 1042/1110] Make the unsafe_sizeof_count_copies lint find copy_{from,to} method calls --- .../src/unsafe_sizeof_count_copies.rs | 66 ++++++++++++++----- tests/ui/unsafe_sizeof_count_copies.rs | 5 ++ tests/ui/unsafe_sizeof_count_copies.stderr | 60 +++++++++++++---- 3 files changed, 99 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs index 2422df8feba..5df7d72564e 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_hir::BinOpKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Ty as TyM, TyS}; +use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { match &expr.kind { ExprKind::Call(ref count_func, _func_args) => { if_chain! { @@ -62,35 +62,65 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let [_src, _dest, count] = &**args; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) + || match_def_path(cx, def_id, &paths::COPY); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; + if_chain! { + // Find calls to copy_{from,to}{,_nonoverlapping} + if let ExprKind::MethodCall(ref method_path, _, ref args, _) = expr.kind; + if let [ptr_self, _, count] = &**args; + let method_ident = method_path.ident.as_str(); + if method_ident== "copy_to" || method_ident == "copy_from" + || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; + + // Get the pointee type + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + cx.typeck_results().expr_ty(ptr_self).kind(); + then { + return Some((pointee_ty, count)); + } + }; + None +} + impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(ref func, ref func_args) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY); + const HELP_MSG: &str = "use a count of elements instead of a count of bytes \ + for the count parameter, it already gets multiplied by the size of the pointed to type"; - // Get the pointee type - let _substs = cx.typeck_results().node_substs(func.hir_id); - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + const LINT_MSG: &str = "unsafe memory copying using a byte count \ + (Multiplied by size_of::) instead of a count of T"; + + if_chain! { + // Find calls to unsafe copy functions and get + // the pointee type and count parameter expression + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); // Find a size_of call in the count parameter expression and // check that it's the same type - if let [_src, _dest, count] = &**func_args; - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count); + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); if TyS::same_type(pointee_ty, ty_used_for_size_of); then { span_lint_and_help( cx, UNSAFE_SIZEOF_COUNT_COPIES, expr.span, - "unsafe memory copying using a byte count (Multiplied by size_of::) \ - instead of a count of T", + LINT_MSG, None, - "use a count of elements instead of a count of bytes for the count parameter, \ - it already gets multiplied by the size of the pointed to type" + HELP_MSG ); } }; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 0077ed07fce..0bb22314cc0 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -14,6 +14,11 @@ fn main() { unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 6804df8cdfc..14ca04617c2 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -18,13 +18,45 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T --> $DIR/unsafe_sizeof_count_copies.rs:17:14 | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:19:14 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:20:14 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + --> $DIR/unsafe_sizeof_count_copies.rs:23:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:21:14 + --> $DIR/unsafe_sizeof_count_copies.rs:26:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +72,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + --> $DIR/unsafe_sizeof_count_copies.rs:27:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +80,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:24:14 + --> $DIR/unsafe_sizeof_count_copies.rs:29:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +88,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:25:14 + --> $DIR/unsafe_sizeof_count_copies.rs:30:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +96,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + --> $DIR/unsafe_sizeof_count_copies.rs:33:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +104,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:29:14 + --> $DIR/unsafe_sizeof_count_copies.rs:34:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +112,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:31:14 + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + --> $DIR/unsafe_sizeof_count_copies.rs:37:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZ = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:35:14 + --> $DIR/unsafe_sizeof_count_copies.rs:40:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 + --> $DIR/unsafe_sizeof_count_copies.rs:41:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:38:14 + --> $DIR/unsafe_sizeof_count_copies.rs:43:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:39:14 + --> $DIR/unsafe_sizeof_count_copies.rs:44:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors From 1b80990fe01646868f85245f608203e23f64184a Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 14:23:59 -0300 Subject: [PATCH 1043/1110] Make the unsafe_sizeof_count_copies lint work with more functions Specifically: - find std::ptr::write_bytes - find std::ptr::swap_nonoverlapping - find std::ptr::slice_from_raw_parts - find std::ptr::slice_from_raw_parts_mut - pointer_primitive::write_bytes --- .../src/unsafe_sizeof_count_copies.rs | 42 ++++-- clippy_lints/src/utils/paths.rs | 4 + tests/ui/unsafe_sizeof_count_copies.rs | 12 +- tests/ui/unsafe_sizeof_count_copies.stderr | 122 ++++++++++++------ 4 files changed, 126 insertions(+), 54 deletions(-) diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs index 5df7d72564e..8a4538091e7 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -41,8 +41,8 @@ declare_clippy_lint! { declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { - match &expr.kind { - ExprKind::Call(ref count_func, _func_args) => { + match expr.kind { + ExprKind::Call(count_func, _func_args) => { if_chain! { if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); @@ -56,7 +56,7 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right)) + get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) }, _ => None, } @@ -64,13 +64,16 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(ref func, ref args) = expr.kind; - if let [_src, _dest, count] = &**args; + // Find calls to ptr::{copy, copy_nonoverlapping} + // and ptr::{swap_nonoverlapping, write_bytes}, + if let ExprKind::Call(func, args) = expr.kind; + if let [_, _, count] = args; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY); + || match_def_path(cx, def_id, &paths::COPY) + || match_def_path(cx, def_id, &paths::WRITE_BYTES) + || match_def_path(cx, def_id, &paths::PTR_SWAP_NONOVERLAPPING); // Get the pointee type if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); @@ -79,11 +82,11 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - } }; if_chain! { - // Find calls to copy_{from,to}{,_nonoverlapping} - if let ExprKind::MethodCall(ref method_path, _, ref args, _) = expr.kind; - if let [ptr_self, _, count] = &**args; + // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; + if let [ptr_self, _, count] = args; let method_ident = method_path.ident.as_str(); - if method_ident== "copy_to" || method_ident == "copy_from" + if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from" || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; // Get the pointee type @@ -93,6 +96,21 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - return Some((pointee_ty, count)); } }; + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(func, args) = expr.kind; + if let [_data, count] = args; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS) + || match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS_MUT); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; None } @@ -102,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { for the count parameter, it already gets multiplied by the size of the pointed to type"; const LINT_MSG: &str = "unsafe memory copying using a byte count \ - (Multiplied by size_of::) instead of a count of T"; + (multiplied by size_of/size_of_val::) instead of a count of T"; if_chain! { // Find calls to unsafe copy functions and get diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index fe763d4bfbb..87c020a99db 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -104,6 +104,9 @@ pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"]; +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"]; +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; @@ -158,3 +161,4 @@ pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; +pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 0bb22314cc0..6aed8c31f7e 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -1,7 +1,9 @@ #![warn(clippy::unsafe_sizeof_count_copies)] use std::mem::{size_of, size_of_val}; -use std::ptr::{copy, copy_nonoverlapping}; +use std::ptr::{ + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, +}; fn main() { const SIZE: usize = 128; @@ -22,6 +24,14 @@ fn main() { unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + + unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 14ca04617c2..6f491bc4e4a 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -1,5 +1,5 @@ -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:14:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:16:14 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,157 +7,197 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:15:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:17:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:17:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:19:14 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:18:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:20:14 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:19:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:21:14 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:20:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:24:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:23:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:25:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:26:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:27:14 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:30:14 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + | +LL | unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:33:14 + | +LL | unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:27:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:37:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:29:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:39:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:30:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:40:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:33:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:43:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:34:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:44:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:46:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:37:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:47:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:40:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:50:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:41:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:51:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:43:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:53:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:44:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:54:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: aborting due to 20 previous errors +error: aborting due to 25 previous errors From 63a3c44060b9b06e10e7a854abcdbb853f6938c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 14:32:11 -0300 Subject: [PATCH 1044/1110] Remove unnecessary unsafe_size_count_copies tests --- tests/ui/unsafe_sizeof_count_copies.rs | 22 +------ tests/ui/unsafe_sizeof_count_copies.stderr | 76 +--------------------- 2 files changed, 3 insertions(+), 95 deletions(-) diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 6aed8c31f7e..2a9adeb6bd9 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -34,36 +34,16 @@ fn main() { // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; // Count expression involving nested multiplications of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; - // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; // No size_of calls (Should not trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; // Different types for pointee and size_of (Should not trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; } diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 6f491bc4e4a..7989e96dd21 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -111,93 +111,21 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:37:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T --> $DIR/unsafe_sizeof_count_copies.rs:39:14 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:40:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:43:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:44:14 - | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:46:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:47:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:50:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:51:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:53:14 + --> $DIR/unsafe_sizeof_count_copies.rs:42:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:54:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: aborting due to 25 previous errors +error: aborting due to 16 previous errors From af9685bb1e7e27a7b21d9939a42c1e9dce8c4df5 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Dec 2020 20:55:38 -0300 Subject: [PATCH 1045/1110] Rename unsafe_sizeof_count_copies to size_of_in_element_count Also fix review comments: - Use const arrays and iterate them for the method/function names - merge 2 if_chain's into one using a rest pattern - remove unnecessary unsafe block in test And make the lint only point to the count expression instead of the entire function call --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +- ..._copies.rs => size_of_in_element_count.rs} | 74 +++++----- ..._copies.rs => size_of_in_element_count.rs} | 9 +- tests/ui/size_of_in_element_count.stderr | 131 ++++++++++++++++++ tests/ui/unsafe_sizeof_count_copies.stderr | 131 ------------------ 6 files changed, 175 insertions(+), 182 deletions(-) rename clippy_lints/src/{unsafe_sizeof_count_copies.rs => size_of_in_element_count.rs} (63%) rename tests/ui/{unsafe_sizeof_count_copies.rs => size_of_in_element_count.rs} (86%) create mode 100644 tests/ui/size_of_in_element_count.stderr delete mode 100644 tests/ui/unsafe_sizeof_count_copies.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e0f3b82ad25..c7e02aaf4e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2057,6 +2057,7 @@ Released 2018-09-13 [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive @@ -2124,7 +2125,6 @@ Released 2018-09-13 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name -[`unsafe_sizeof_count_copies`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_sizeof_count_copies [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1bce0130b40..06961064a4b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -306,6 +306,7 @@ mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; +mod size_of_in_element_count; mod slow_vector_initialization; mod stable_sort_primitive; mod strings; @@ -329,7 +330,6 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; -mod unsafe_sizeof_count_copies; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -917,7 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES, + &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -1000,7 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); - store.register_late_pass(|| box unsafe_sizeof_count_copies::UnsafeSizeofCountCopies); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -1608,7 +1608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1887,7 +1887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/size_of_in_element_count.rs similarity index 63% rename from clippy_lints/src/unsafe_sizeof_count_copies.rs rename to clippy_lints/src/size_of_in_element_count.rs index 8a4538091e7..9701e793700 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -1,5 +1,5 @@ -//! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee -//! count +//! Lint on use of `size_of` or `size_of_val` of T in an expression +//! expecting a count of T use crate::utils::{match_def_path, paths, span_lint_and_help}; use if_chain::if_chain; @@ -11,15 +11,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Detects expressions where - /// size_of:: is used as the count argument to unsafe - /// memory copying functions like ptr::copy and - /// ptr::copy_nonoverlapping where T is the pointee type - /// of the pointers used + /// size_of:: or size_of_val:: is used as a + /// count of elements of type T /// /// **Why is this bad?** These functions expect a count - /// of T and not a number of bytes, which can lead to - /// copying the incorrect amount of bytes, which can - /// result in Undefined Behaviour + /// of T and not a number of bytes /// /// **Known problems:** None. /// @@ -33,12 +29,12 @@ declare_clippy_lint! { /// let mut y = [2u8; SIZE]; /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; /// ``` - pub UNSAFE_SIZEOF_COUNT_COPIES, + pub SIZE_OF_IN_ELEMENT_COUNT, correctness, - "unsafe memory copying using a byte count instead of a count of T" + "using size_of:: or size_of_val:: where a count of elements of T is expected" } -declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); +declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { match expr.kind { @@ -62,18 +58,30 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { if_chain! { // Find calls to ptr::{copy, copy_nonoverlapping} // and ptr::{swap_nonoverlapping, write_bytes}, if let ExprKind::Call(func, args) = expr.kind; - if let [_, _, count] = args; + if let [.., count] = args; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY) - || match_def_path(cx, def_id, &paths::WRITE_BYTES) - || match_def_path(cx, def_id, &paths::PTR_SWAP_NONOVERLAPPING); + if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); // Get the pointee type if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); @@ -86,8 +94,7 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; if let [ptr_self, _, count] = args; let method_ident = method_path.ident.as_str(); - if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from" - || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; + if METHODS.iter().any(|m| *m == &*method_ident); // Get the pointee type if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = @@ -96,31 +103,16 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - return Some((pointee_ty, count)); } }; - if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(func, args) = expr.kind; - if let [_data, count] = args; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS) - || match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS_MUT); - - // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } - }; None } -impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { +impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - const HELP_MSG: &str = "use a count of elements instead of a count of bytes \ - for the count parameter, it already gets multiplied by the size of the pointed to type"; + const HELP_MSG: &str = "use a count of elements instead of a count of bytes\ + , it already gets multiplied by the size of the type"; - const LINT_MSG: &str = "unsafe memory copying using a byte count \ - (multiplied by size_of/size_of_val::) instead of a count of T"; + const LINT_MSG: &str = "found a count of bytes \ + instead of a count of elements of T"; if_chain! { // Find calls to unsafe copy functions and get @@ -134,8 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { then { span_lint_and_help( cx, - UNSAFE_SIZEOF_COUNT_COPIES, - expr.span, + SIZE_OF_IN_ELEMENT_COUNT, + count_expr.span, LINT_MSG, None, HELP_MSG diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/size_of_in_element_count.rs similarity index 86% rename from tests/ui/unsafe_sizeof_count_copies.rs rename to tests/ui/size_of_in_element_count.rs index 2a9adeb6bd9..d4658ebf72d 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/size_of_in_element_count.rs @@ -1,8 +1,9 @@ -#![warn(clippy::unsafe_sizeof_count_copies)] +#![warn(clippy::size_of_in_element_count)] use std::mem::{size_of, size_of_val}; use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, + copy, copy_nonoverlapping, slice_from_raw_parts, + slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, }; fn main() { @@ -29,8 +30,8 @@ fn main() { unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr new file mode 100644 index 00000000000..80c3fec1b05 --- /dev/null +++ b/tests/ui/size_of_in_element_count.stderr @@ -0,0 +1,131 @@ +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:17:68 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:18:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:20:49 + | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:21:64 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:22:51 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:23:66 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:25:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:26:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:28:46 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:29:47 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:31:66 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:33:46 + | +LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:34:38 + | +LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:37:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:40:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:43:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 16 previous errors + diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr deleted file mode 100644 index 7989e96dd21..00000000000 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ /dev/null @@ -1,131 +0,0 @@ -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:16:14 - | -LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:17:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:19:14 - | -LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:20:14 - | -LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:21:14 - | -LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 - | -LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:24:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:25:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:27:14 - | -LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:28:14 - | -LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:30:14 - | -LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:32:14 - | -LL | unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:33:14 - | -LL | unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:39:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:42:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: aborting due to 16 previous errors - From c1a5329475d041dbeb077ecda6ae71f690b4bcc1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Nov 2020 21:54:50 -0300 Subject: [PATCH 1046/1110] Add more functions to size_of_in_element_count Specifically ptr::{sub, wrapping_sub, add, wrapping_add, offset, wrapping_offset} and slice::{from_raw_parts, from_raw_parts_mut} The lint now also looks for size_of calls through casts (Since offset takes an isize) --- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/size_of_in_element_count.rs | 50 +++++----- clippy_lints/src/utils/paths.rs | 2 + tests/ui/size_of_in_element_count.rs | 15 ++- tests/ui/size_of_in_element_count.stderr | 98 ++++++++++++++++---- 5 files changed, 128 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 06961064a4b..4ef595bcffd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -848,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, + &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, @@ -917,7 +918,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -1562,6 +1562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), @@ -1608,7 +1609,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1872,6 +1872,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1887,7 +1888,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 9701e793700..210cf5773e1 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -54,31 +54,40 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) }, + ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr), _ => None, } } -const FUNCTIONS: [[&str; 3]; 6] = [ - paths::COPY_NONOVERLAPPING, - paths::COPY, - paths::WRITE_BYTES, - paths::PTR_SWAP_NONOVERLAPPING, - paths::PTR_SLICE_FROM_RAW_PARTS, - paths::PTR_SLICE_FROM_RAW_PARTS_MUT, - ]; -const METHODS: [&str; 5] = [ - "write_bytes", - "copy_to", - "copy_from", - "copy_to_nonoverlapping", - "copy_from_nonoverlapping", - ]; fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + const FUNCTIONS: [&[&str]; 8] = [ + &paths::COPY_NONOVERLAPPING, + &paths::COPY, + &paths::WRITE_BYTES, + &paths::PTR_SWAP_NONOVERLAPPING, + &paths::PTR_SLICE_FROM_RAW_PARTS, + &paths::PTR_SLICE_FROM_RAW_PARTS_MUT, + &paths::SLICE_FROM_RAW_PARTS, + &paths::SLICE_FROM_RAW_PARTS_MUT, + ]; + const METHODS: [&str; 11] = [ + "write_bytes", + "copy_to", + "copy_from", + "copy_to_nonoverlapping", + "copy_from_nonoverlapping", + "add", + "wrapping_add", + "sub", + "wrapping_sub", + "offset", + "wrapping_offset", + ]; + if_chain! { // Find calls to ptr::{copy, copy_nonoverlapping} // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, args) = expr.kind; - if let [.., count] = args; + if let ExprKind::Call(func, [.., count]) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); @@ -91,13 +100,12 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - }; if_chain! { // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; - if let [ptr_self, _, count] = args; + if let ExprKind::MethodCall(method_path, _, [ptr_self, .., count], _) = expr.kind; let method_ident = method_path.ident.as_str(); if METHODS.iter().any(|m| *m == &*method_ident); // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = cx.typeck_results().expr_ty(ptr_self).kind(); then { return Some((pointee_ty, count)); @@ -115,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { instead of a count of elements of T"; if_chain! { - // Find calls to unsafe copy functions and get + // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 87c020a99db..6fdc7b4587f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -128,6 +128,8 @@ pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGu pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; diff --git a/tests/ui/size_of_in_element_count.rs b/tests/ui/size_of_in_element_count.rs index d4658ebf72d..b13e390705a 100644 --- a/tests/ui/size_of_in_element_count.rs +++ b/tests/ui/size_of_in_element_count.rs @@ -1,10 +1,11 @@ #![warn(clippy::size_of_in_element_count)] +#![allow(clippy::ptr_offset_with_cast)] use std::mem::{size_of, size_of_val}; use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, - slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, }; +use std::slice::{from_raw_parts, from_raw_parts_mut}; fn main() { const SIZE: usize = 128; @@ -33,6 +34,16 @@ fn main() { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + + unsafe { y.as_mut_ptr().sub(size_of::()) }; + y.as_ptr().wrapping_sub(size_of::()); + unsafe { y.as_ptr().add(size_of::()) }; + y.as_mut_ptr().wrapping_add(size_of::()); + unsafe { y.as_ptr().offset(size_of::() as isize) }; + y.as_mut_ptr().wrapping_offset(size_of::() as isize); + // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr index 80c3fec1b05..b7f421ec997 100644 --- a/tests/ui/size_of_in_element_count.stderr +++ b/tests/ui/size_of_in_element_count.stderr @@ -1,5 +1,5 @@ error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:17:68 + --> $DIR/size_of_in_element_count.rs:18:68 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:18:62 + --> $DIR/size_of_in_element_count.rs:19:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:20:49 + --> $DIR/size_of_in_element_count.rs:21:49 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:21:64 + --> $DIR/size_of_in_element_count.rs:22:64 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of:: $DIR/size_of_in_element_count.rs:22:51 + --> $DIR/size_of_in_element_count.rs:23:51 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:23:66 + --> $DIR/size_of_in_element_count.rs:24:66 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::< = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:25:47 + --> $DIR/size_of_in_element_count.rs:26:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:26:47 + --> $DIR/size_of_in_element_count.rs:27:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:28:46 + --> $DIR/size_of_in_element_count.rs:29:46 | LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:29:47 + --> $DIR/size_of_in_element_count.rs:30:47 | LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:31:66 + --> $DIR/size_of_in_element_count.rs:32:66 | LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::< = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:33:46 + --> $DIR/size_of_in_element_count.rs:34:46 | LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:34:38 + --> $DIR/size_of_in_element_count.rs:35:38 | LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,71 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:37:62 + --> $DIR/size_of_in_element_count.rs:37:49 + | +LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:38:41 + | +LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:40:33 + | +LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:41:29 + | +LL | y.as_ptr().wrapping_sub(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:42:29 + | +LL | unsafe { y.as_ptr().add(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:43:33 + | +LL | y.as_mut_ptr().wrapping_add(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:44:32 + | +LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:45:36 + | +LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:48:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +176,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:40:62 + --> $DIR/size_of_in_element_count.rs:51:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +184,12 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:43:47 + --> $DIR/size_of_in_element_count.rs:54:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: aborting due to 16 previous errors +error: aborting due to 24 previous errors From 5f821fbcf1687c4476c117bcab5a3b2a4a977d4c Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 19:41:44 -0500 Subject: [PATCH 1047/1110] Added test to make sure ignoring the error with a named wildcard value works --- tests/ui/map_err.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 05b9949f102..00e037843f8 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -22,5 +22,9 @@ fn main() -> Result<(), Errors> { println!("{:?}", x.map_err(|_| Errors::Ignored)); + // Should not warn you because you explicitly ignore the parameter + // using a named wildcard value + println!("{:?}", x.map_err(|_foo| Errors::Ignored)); + Ok(()) } From 01f32116028a127b4a946c72b8ed5de3e03be477 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 4 Dec 2020 10:01:09 +0100 Subject: [PATCH 1048/1110] Turn unnecessary_wraps applicability to MaybeIncorrect --- clippy_lints/src/unnecessary_wraps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 360df2a6752..5d801511a0b 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { diag.multipart_suggestion( "...and change the returning expressions", suggs, - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); }, ); From 75140e813f3701e76ab64e091653395ec397f68d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 4 Dec 2020 23:36:07 +0900 Subject: [PATCH 1049/1110] Fix a style of texts in `size_of_in_element_count` --- clippy_lints/src/size_of_in_element_count.rs | 11 ++--- tests/ui/size_of_in_element_count.stderr | 48 ++++++++++---------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 210cf5773e1..ea7a76146f5 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -11,11 +11,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Detects expressions where - /// size_of:: or size_of_val:: is used as a - /// count of elements of type T + /// `size_of::` or `size_of_val::` is used as a + /// count of elements of type `T` /// /// **Why is this bad?** These functions expect a count - /// of T and not a number of bytes + /// of `T` and not a number of bytes /// /// **Known problems:** None. /// @@ -23,7 +23,6 @@ declare_clippy_lint! { /// ```rust,no_run /// # use std::ptr::copy_nonoverlapping; /// # use std::mem::size_of; - /// /// const SIZE: usize = 128; /// let x = [2u8; SIZE]; /// let mut y = [2u8; SIZE]; @@ -31,7 +30,7 @@ declare_clippy_lint! { /// ``` pub SIZE_OF_IN_ELEMENT_COUNT, correctness, - "using size_of:: or size_of_val:: where a count of elements of T is expected" + "using `size_of::` or `size_of_val::` where a count of elements of `T` is expected" } declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); @@ -120,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { , it already gets multiplied by the size of the type"; const LINT_MSG: &str = "found a count of bytes \ - instead of a count of elements of T"; + instead of a count of elements of `T`"; if_chain! { // Find calls to functions with an element count parameter and get diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr index b7f421ec997..8cf3612abda 100644 --- a/tests/ui/size_of_in_element_count.stderr +++ b/tests/ui/size_of_in_element_count.stderr @@ -1,4 +1,4 @@ -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:18:68 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; @@ -7,7 +7,7 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:19:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; @@ -15,7 +15,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:21:49 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; @@ -23,7 +23,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:22:64 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; @@ -31,7 +31,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of:: $DIR/size_of_in_element_count.rs:23:51 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; @@ -39,7 +39,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:24:66 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; @@ -47,7 +47,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::< | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:26:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; @@ -55,7 +55,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:27:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; @@ -63,7 +63,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:29:46 | LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; @@ -71,7 +71,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:30:47 | LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; @@ -79,7 +79,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:32:66 | LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; @@ -87,7 +87,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::< | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:34:46 | LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); @@ -95,7 +95,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:35:38 | LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); @@ -103,7 +103,7 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:37:49 | LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; @@ -111,7 +111,7 @@ LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:38:41 | LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; @@ -119,7 +119,7 @@ LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:40:33 | LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; @@ -127,7 +127,7 @@ LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:41:29 | LL | y.as_ptr().wrapping_sub(size_of::()); @@ -135,7 +135,7 @@ LL | y.as_ptr().wrapping_sub(size_of::()); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:42:29 | LL | unsafe { y.as_ptr().add(size_of::()) }; @@ -143,7 +143,7 @@ LL | unsafe { y.as_ptr().add(size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:43:33 | LL | y.as_mut_ptr().wrapping_add(size_of::()); @@ -151,7 +151,7 @@ LL | y.as_mut_ptr().wrapping_add(size_of::()); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:44:32 | LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; @@ -159,7 +159,7 @@ LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:45:36 | LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); @@ -167,7 +167,7 @@ LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:48:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; @@ -175,7 +175,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:51:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; @@ -183,7 +183,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:54:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; From 6edd59885605d2cf0aa8727cf2cd30cd13098804 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 4 Dec 2020 21:26:47 +0000 Subject: [PATCH 1050/1110] Added a lint-fraction-readability flag to the configuration --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/literal_representation.rs | 37 ++++++++++++---- clippy_lints/src/utils/conf.rs | 2 + .../lint_decimal_readability/clippy.toml | 1 + .../ui-toml/lint_decimal_readability/test.rs | 22 ++++++++++ .../lint_decimal_readability/test.stderr | 10 +++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/unreadable_literal.fixed | 18 ++++++-- tests/ui/unreadable_literal.rs | 18 ++++++-- tests/ui/unreadable_literal.stderr | 42 +++++++++++-------- 10 files changed, 118 insertions(+), 37 deletions(-) create mode 100644 tests/ui-toml/lint_decimal_readability/clippy.toml create mode 100644 tests/ui-toml/lint_decimal_readability/test.rs create mode 100644 tests/ui-toml/lint_decimal_readability/test.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a861a34aeb7..58e91dd32bd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,7 +1138,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); + let literal_representation_lint_fraction_readability = conf.lint_fraction_readability; + store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); let enum_variant_name_threshold = conf.enum_variant_name_threshold; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index e8a741683da..3920e5b6e83 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -11,7 +11,7 @@ use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// **What it does:** Warns if a long integral or floating-point constant does @@ -32,7 +32,7 @@ declare_clippy_lint! { /// ``` pub UNREADABLE_LITERAL, pedantic, - "long integer literal without underscores" + "long literal without underscores" } declare_clippy_lint! { @@ -208,7 +208,13 @@ impl WarningType { } } -declare_lint_pass!(LiteralDigitGrouping => [ +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct LiteralDigitGrouping { + lint_fraction_readability: bool, +} + +impl_lint_pass!(LiteralDigitGrouping => [ UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, @@ -223,7 +229,7 @@ impl EarlyLintPass for LiteralDigitGrouping { } if let ExprKind::Lit(ref lit) = expr.kind { - Self::check_lit(cx, lit) + self.check_lit(cx, lit) } } } @@ -232,7 +238,13 @@ impl EarlyLintPass for LiteralDigitGrouping { const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; impl LiteralDigitGrouping { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + pub fn new(lint_fraction_readability: bool) -> Self { + Self { + lint_fraction_readability, + } + } + + fn check_lit(&self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); @@ -247,9 +259,12 @@ impl LiteralDigitGrouping { let result = (|| { - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?; + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?; + let fractional_group_size = Self::get_group_size( + fraction.rsplit('_'), + num_lit.radix, + self.lint_fraction_readability)?; let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, @@ -363,7 +378,11 @@ impl LiteralDigitGrouping { /// Returns the size of the digit groups (or None if ungrouped) if successful, /// otherwise returns a `WarningType` for linting. - fn get_group_size<'a>(groups: impl Iterator, radix: Radix) -> Result, WarningType> { + fn get_group_size<'a>( + groups: impl Iterator, + radix: Radix, + lint_unreadable: bool, + ) -> Result, WarningType> { let mut groups = groups.map(str::len); let first = groups.next().expect("At least one group"); @@ -380,7 +399,7 @@ impl LiteralDigitGrouping { } else { Ok(Some(second)) } - } else if first > 5 { + } else if first > 5 && lint_unreadable { Err(WarningType::UnreadableLiteral) } else { Ok(None) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index fc6304118d9..c9d5f781f1b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -170,6 +170,8 @@ define_Conf! { (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), + /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. + (lint_fraction_readability, "lint_fraction_readability": bool, true), } impl Default for Conf { diff --git a/tests/ui-toml/lint_decimal_readability/clippy.toml b/tests/ui-toml/lint_decimal_readability/clippy.toml new file mode 100644 index 00000000000..635e282dc06 --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/clippy.toml @@ -0,0 +1 @@ +lint-fraction-readability = false \ No newline at end of file diff --git a/tests/ui-toml/lint_decimal_readability/test.rs b/tests/ui-toml/lint_decimal_readability/test.rs new file mode 100644 index 00000000000..9377eb69b23 --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/test.rs @@ -0,0 +1,22 @@ +#[deny(clippy::unreadable_literal)] + +fn allow_inconsistent_digit_grouping() { + #![allow(clippy::inconsistent_digit_grouping)] + let _pass1 = 100_200_300.123456789; +} + +fn main() { + allow_inconsistent_digit_grouping(); + + let _pass1 = 100_200_300.100_200_300; + let _pass2 = 1.123456789; + let _pass3 = 1.0; + let _pass4 = 10000.00001; + let _pass5 = 1.123456789e1; + + // due to clippy::inconsistent-digit-grouping + let _fail1 = 100_200_300.123456789; + + // fail due to the integer part + let _fail2 = 100200300.300200100; +} diff --git a/tests/ui-toml/lint_decimal_readability/test.stderr b/tests/ui-toml/lint_decimal_readability/test.stderr new file mode 100644 index 00000000000..9119ef19a7b --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/test.stderr @@ -0,0 +1,10 @@ +error: digits grouped inconsistently by underscores + --> $DIR/test.rs:18:18 + | +LL | let _fail1 = 100_200_300.123456789; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index af3d9ecf6e8..eff4da7e658 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `lint-fraction-readability`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 4043d53299f..c2e38037add 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -10,6 +10,14 @@ macro_rules! foo { }; } +struct Bar(f32); + +macro_rules! bar { + () => { + Bar(100200300400.100200300400500) + }; +} + fn main() { let _good = ( 0b1011_i64, @@ -26,10 +34,12 @@ fn main() { let _good_sci = 1.1234e1; let _bad_sci = 1.123_456e1; - let _fail9 = 0x00ab_cdef; - let _fail10: u32 = 0xBAFE_BAFE; - let _fail11 = 0x0abc_deff; - let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail1 = 0x00ab_cdef; + let _fail2: u32 = 0xBAFE_BAFE; + let _fail3 = 0x0abc_deff; + let _fail4: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail5 = 1.100_300_400; let _ = foo!(); + let _ = bar!(); } diff --git a/tests/ui/unreadable_literal.rs b/tests/ui/unreadable_literal.rs index e658a5f28c9..8296945b25e 100644 --- a/tests/ui/unreadable_literal.rs +++ b/tests/ui/unreadable_literal.rs @@ -10,6 +10,14 @@ macro_rules! foo { }; } +struct Bar(f32); + +macro_rules! bar { + () => { + Bar(100200300400.100200300400500) + }; +} + fn main() { let _good = ( 0b1011_i64, @@ -26,10 +34,12 @@ fn main() { let _good_sci = 1.1234e1; let _bad_sci = 1.123456e1; - let _fail9 = 0xabcdef; - let _fail10: u32 = 0xBAFEBAFE; - let _fail11 = 0xabcdeff; - let _fail12: i128 = 0xabcabcabcabcabcabc; + let _fail1 = 0xabcdef; + let _fail2: u32 = 0xBAFEBAFE; + let _fail3 = 0xabcdeff; + let _fail4: i128 = 0xabcabcabcabcabcabc; + let _fail5 = 1.100300400; let _ = foo!(); + let _ = bar!(); } diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 8645cabeabb..8436aac17ac 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,5 +1,5 @@ error: digits of hex or binary literal not grouped by four - --> $DIR/unreadable_literal.rs:17:9 + --> $DIR/unreadable_literal.rs:25:9 | LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` @@ -7,7 +7,7 @@ LL | 0x1_234_567, = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:17 + --> $DIR/unreadable_literal.rs:33:17 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` @@ -15,52 +15,58 @@ LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); = note: `-D clippy::unreadable-literal` implied by `-D warnings` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:31 + --> $DIR/unreadable_literal.rs:33:31 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:49 + --> $DIR/unreadable_literal.rs:33:49 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^ help: consider: `123_456_f32` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:61 + --> $DIR/unreadable_literal.rs:33:61 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:27:20 + --> $DIR/unreadable_literal.rs:35:20 | LL | let _bad_sci = 1.123456e1; | ^^^^^^^^^^ help: consider: `1.123_456e1` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:29:18 + --> $DIR/unreadable_literal.rs:37:18 | -LL | let _fail9 = 0xabcdef; +LL | let _fail1 = 0xabcdef; | ^^^^^^^^ help: consider: `0x00ab_cdef` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:30:24 + --> $DIR/unreadable_literal.rs:38:23 | -LL | let _fail10: u32 = 0xBAFEBAFE; - | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` +LL | let _fail2: u32 = 0xBAFEBAFE; + | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:31:19 + --> $DIR/unreadable_literal.rs:39:18 | -LL | let _fail11 = 0xabcdeff; - | ^^^^^^^^^ help: consider: `0x0abc_deff` +LL | let _fail3 = 0xabcdeff; + | ^^^^^^^^^ help: consider: `0x0abc_deff` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:32:25 + --> $DIR/unreadable_literal.rs:40:24 | -LL | let _fail12: i128 = 0xabcabcabcabcabcabc; - | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` +LL | let _fail4: i128 = 0xabcabcabcabcabcabc; + | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` -error: aborting due to 10 previous errors +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:41:18 + | +LL | let _fail5 = 1.100300400; + | ^^^^^^^^^^^ help: consider: `1.100_300_400` + +error: aborting due to 11 previous errors From 18383c69c1ebf0b6bc0f1ecdeee062f0767a69e8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 4 Dec 2020 22:05:52 +0000 Subject: [PATCH 1051/1110] Updated code for CI --- clippy_lints/src/literal_representation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 3920e5b6e83..87a957a9bd2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -244,7 +244,7 @@ impl LiteralDigitGrouping { } } - fn check_lit(&self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); From 898b7c594cfdf1eb56d24a4c6fa02678cf5029a8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 5 Dec 2020 20:59:53 +0000 Subject: [PATCH 1052/1110] Renamed the configuraiton to unreadable-literal-lint-fractions --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/lint_decimal_readability/clippy.toml | 2 +- tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58e91dd32bd..2b99ed570b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,7 +1138,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - let literal_representation_lint_fraction_readability = conf.lint_fraction_readability; + let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c9d5f781f1b..6403ff6dad1 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -171,7 +171,7 @@ define_Conf! { /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. - (lint_fraction_readability, "lint_fraction_readability": bool, true), + (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true), } impl Default for Conf { diff --git a/tests/ui-toml/lint_decimal_readability/clippy.toml b/tests/ui-toml/lint_decimal_readability/clippy.toml index 635e282dc06..6feaf7d5c0c 100644 --- a/tests/ui-toml/lint_decimal_readability/clippy.toml +++ b/tests/ui-toml/lint_decimal_readability/clippy.toml @@ -1 +1 @@ -lint-fraction-readability = false \ No newline at end of file +unreadable-literal-lint-fractions = false \ No newline at end of file diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index eff4da7e658..7b3c476461d 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `lint-fraction-readability`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `third-party` at line 5 column 1 error: aborting due to previous error From e90b977a082eac4283a52b53d4da3a507a8ee9a0 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 22 Nov 2020 22:04:18 +0900 Subject: [PATCH 1053/1110] Fix FP in `unnecessary_lazy_evaluations` --- clippy_lints/src/utils/usage.rs | 21 ++++++++++++++------- tests/ui/unnecessary_lazy_eval_unfixable.rs | 4 ++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index a7d0ea6ccfb..fc0db7f64ec 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector { } impl<'tcx> ParamBindingIdCollector { fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { - let mut finder = ParamBindingIdCollector { - binding_hir_ids: Vec::new(), - }; - finder.visit_body(body); - finder.binding_hir_ids + let mut hir_ids: Vec = Vec::new(); + for param in body.params.iter() { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_param(param); + for hir_id in &finder.binding_hir_ids { + hir_ids.push(*hir_id); + } + } + hir_ids } } impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { type Map = Map<'tcx>; - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.binding_hir_ids.push(hir_id); } + intravisit::walk_pat(self, pat); } fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs index 2e923bc97a2..b05dd143bfd 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -15,4 +15,8 @@ fn main() { } let _ = Ok(1).unwrap_or_else(|e::E| 2); let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); + + // Fix #6343 + let arr = [(Some(1),)]; + Some(&0).and_then(|&i| arr[i].0); } From ba1249465c036b2ccc6daf34749ea9d5df77f358 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Dec 2020 16:45:10 +0900 Subject: [PATCH 1054/1110] cargo dev fmt --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 7 ++++--- clippy_lints/src/pass_by_ref_or_value.rs | 7 ++++--- clippy_lints/src/redundant_clone.rs | 5 ++++- clippy_lints/src/types.rs | 4 +++- clippy_lints/src/unnecessary_wraps.rs | 5 ++++- clippy_lints/src/utils/ast_utils.rs | 5 ++++- clippy_lints/src/utils/mod.rs | 2 +- 9 files changed, 26 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index edecba57e44..55e4755c278 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -480,7 +480,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { | ItemKind::ForeignMod(..) => return false, // We found a main function ... ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { - let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 25245b3dbf0..38e2ce563ee 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let has_const_generic_params = generics .params .iter() - .any(|param| matches!(param.kind, GenericParamKind::Const{ .. })); + .any(|param| matches!(param.kind, GenericParamKind::Const { .. })); if already_const(header) || has_const_generic_params { return; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 532c0266946..5043b7b1fc3 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -90,9 +90,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index f03facc235e..6a17d654ac9 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -244,9 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index f0e507105a6..06adbb523d7 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -390,7 +390,10 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { let local = place.local; if local == self.used.0 - && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)) + && !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { self.used.1 = true; } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 74ba53e6a9a..fd74783335d 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1104,7 +1104,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], expr: None, .. + stmts: &[], + expr: None, + .. }, _, ) diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5d801511a0b..e763da593d4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -74,7 +74,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 31b4e25411b..f0267e4c792 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -408,7 +408,10 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { - matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))) + matches!( + (l, r), + (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) + ) } pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a6b64c90e8..007e72d129f 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1500,7 +1500,7 @@ pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. }) + matches!(item.kind, ItemKind::Impl { of_trait: Some(_), .. }) } else { false } From b81141cfb91cbf39e87e32a27530537f18e85405 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 22 Nov 2020 19:02:57 +0100 Subject: [PATCH 1055/1110] Add lint print_stderr Resolves #6348 Almost identical to print_stdout, this lint applies to the `eprintln!` and `eprint!` macros rather than `println!` and `print!`. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/write.rs | 23 +++++++++++++++++++++++ tests/ui/print_stderr.rs | 6 ++++++ tests/ui/print_stderr.stderr | 16 ++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 tests/ui/print_stderr.rs create mode 100644 tests/ui/print_stderr.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e02aaf4e1..1f2c4f310de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2006,6 +2006,7 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr [`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2b99ed570b1..0e630a352fe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -934,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &wildcard_imports::WILDCARD_IMPORTS, &write::PRINTLN_EMPTY_STRING, &write::PRINT_LITERAL, + &write::PRINT_STDERR, &write::PRINT_STDOUT, &write::PRINT_WITH_NEWLINE, &write::USE_DEBUG, @@ -1247,6 +1248,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(&write::PRINT_STDERR), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), ]); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index ff414f748ef..2248bc1e32e 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -75,6 +75,24 @@ declare_clippy_lint! { "printing on stdout" } +declare_clippy_lint! { + /// **What it does:** Checks for printing on *stderr*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// **Why is this bad?** People often print on *stderr* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// **Known problems:** Only catches `eprint!` and `eprintln!` calls. + /// + /// **Example:** + /// ```rust + /// eprintln!("Hello world!"); + /// ``` + pub PRINT_STDERR, + restriction, + "printing on stderr" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `Debug` formatting. The purpose of this /// lint is to catch debugging remnants. @@ -201,6 +219,7 @@ impl_lint_pass!(Write => [ PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, PRINT_STDOUT, + PRINT_STDERR, USE_DEBUG, PRINT_LITERAL, WRITE_WITH_NEWLINE, @@ -260,6 +279,10 @@ impl EarlyLintPass for Write { ); } } + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + } else if mac.path == sym!(eprint) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs new file mode 100644 index 00000000000..e53f46b1c2f --- /dev/null +++ b/tests/ui/print_stderr.rs @@ -0,0 +1,6 @@ +#![warn(clippy::print_stderr)] + +fn main() { + eprintln!("Hello"); + eprint!("World"); +} diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr new file mode 100644 index 00000000000..7252fce72c6 --- /dev/null +++ b/tests/ui/print_stderr.stderr @@ -0,0 +1,16 @@ +error: use of `eprintln!` + --> $DIR/print_stderr.rs:4:5 + | +LL | eprintln!("Hello"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stderr` implied by `-D warnings` + +error: use of `eprint!` + --> $DIR/print_stderr.rs:5:5 + | +LL | eprint!("World"); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 51cee15be0f67403eedfe88c0c4ca07f306bef42 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 29 Nov 2020 09:55:47 +0100 Subject: [PATCH 1056/1110] Add negative tests --- tests/ui/print_stderr.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs index e53f46b1c2f..fa07e74a7be 100644 --- a/tests/ui/print_stderr.rs +++ b/tests/ui/print_stderr.rs @@ -2,5 +2,7 @@ fn main() { eprintln!("Hello"); + println!("This should not do anything"); eprint!("World"); + print!("Nor should this"); } From b04bfbd09b63d978f1bb7f4c9222cb2d37607309 Mon Sep 17 00:00:00 2001 From: Josias Date: Tue, 1 Dec 2020 11:29:49 +0100 Subject: [PATCH 1057/1110] Fix print_stderr.stderr test --- tests/ui/print_stderr.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr index 7252fce72c6..5af735af657 100644 --- a/tests/ui/print_stderr.stderr +++ b/tests/ui/print_stderr.stderr @@ -7,7 +7,7 @@ LL | eprintln!("Hello"); = note: `-D clippy::print-stderr` implied by `-D warnings` error: use of `eprint!` - --> $DIR/print_stderr.rs:5:5 + --> $DIR/print_stderr.rs:6:5 | LL | eprint!("World"); | ^^^^^^^^^^^^^^^^ From 7063c36c912990bd67327a41445706a451fe5b48 Mon Sep 17 00:00:00 2001 From: Josias Date: Fri, 4 Dec 2020 15:39:09 +0100 Subject: [PATCH 1058/1110] Add eprint! to print_with_newline lint --- clippy_lints/src/write.rs | 20 +++++ tests/ui/eprint_with_newline.rs | 49 +++++++++++ tests/ui/eprint_with_newline.stderr | 121 ++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 tests/ui/eprint_with_newline.rs create mode 100644 tests/ui/eprint_with_newline.stderr diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 2248bc1e32e..0fd56f78572 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -283,6 +283,26 @@ impl EarlyLintPass for Write { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); } else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + "using `eprint!()` with a format string that ends in a single newline", + |err| { + err.multipart_suggestion( + "use `eprintln!` instead", + vec![ + (mac.path.span, String::from("eprintln")), + (newline_span(&fmt_str), String::new()), + ], + Applicability::MachineApplicable, + ); + }, + ); + } + } } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/eprint_with_newline.rs b/tests/ui/eprint_with_newline.rs new file mode 100644 index 00000000000..8df32649ad9 --- /dev/null +++ b/tests/ui/eprint_with_newline.rs @@ -0,0 +1,49 @@ +#![allow(clippy::print_literal)] +#![warn(clippy::print_with_newline)] + +fn main() { + eprint!("Hello\n"); + eprint!("Hello {}\n", "world"); + eprint!("Hello {} {}\n", "world", "#2"); + eprint!("{}\n", 1265); + eprint!("\n"); + + // these are all fine + eprint!(""); + eprint!("Hello"); + eprintln!("Hello"); + eprintln!("Hello\n"); + eprintln!("Hello {}\n", "world"); + eprint!("Issue\n{}", 1265); + eprint!("{}", 1265); + eprint!("\n{}", 1275); + eprint!("\n\n"); + eprint!("like eof\n\n"); + eprint!("Hello {} {}\n\n", "world", "#2"); + eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + eprintln!("\nbla\n\n"); // #3126 + + // Escaping + eprint!("\\n"); // #3514 + eprint!("\\\n"); // should fail + eprint!("\\\\n"); + + // Raw strings + eprint!(r"\n"); // #3778 + + // Literal newlines should also fail + eprint!( + " +" + ); + eprint!( + r" +" + ); + + // Don't warn on CRLF (#4208) + eprint!("\r\n"); + eprint!("foo\r\n"); + eprint!("\\r\n"); //~ ERROR + eprint!("foo\rbar\n") // ~ ERROR +} diff --git a/tests/ui/eprint_with_newline.stderr b/tests/ui/eprint_with_newline.stderr new file mode 100644 index 00000000000..31811d1d92a --- /dev/null +++ b/tests/ui/eprint_with_newline.stderr @@ -0,0 +1,121 @@ +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:5:5 + | +LL | eprint!("Hello/n"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-with-newline` implied by `-D warnings` +help: use `eprintln!` instead + | +LL | eprintln!("Hello"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:6:5 + | +LL | eprint!("Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {}", "world"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:7:5 + | +LL | eprint!("Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {} {}", "world", "#2"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:8:5 + | +LL | eprint!("{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("{}", 1265); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:9:5 + | +LL | eprint!("/n"); + | ^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!(); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:28:5 + | +LL | eprint!("//n"); // should fail + | ^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/"); // should fail + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:35:5 + | +LL | / eprint!( +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | "" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:39:5 + | +LL | / eprint!( +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | r"" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:47:5 + | +LL | eprint!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/r"); //~ ERROR + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:48:5 + | +LL | eprint!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("foo/rbar") // ~ ERROR + | ^^^^^^^^ -- + +error: aborting due to 10 previous errors + From e58c7dd1685cb19e2ec87568cbc2cbd0aba3e2c7 Mon Sep 17 00:00:00 2001 From: Dobe Peter Date: Sat, 31 Oct 2020 20:31:34 +0100 Subject: [PATCH 1059/1110] panic_in_result_fn: Extend to also check usages of [debug_]assert* macros Also, the macro-finding logic has been moved to the util module, for use by future lints. --- clippy_lints/src/panic_in_result_fn.rs | 68 +++++++++---------- clippy_lints/src/utils/mod.rs | 33 ++++++++- tests/ui/panic_in_result_fn.stderr | 24 +++---- tests/ui/panic_in_result_fn_assertions.rs | 48 +++++++++++++ tests/ui/panic_in_result_fn_assertions.stderr | 57 ++++++++++++++++ .../ui/panic_in_result_fn_debug_assertions.rs | 48 +++++++++++++ ...panic_in_result_fn_debug_assertions.stderr | 57 ++++++++++++++++ 7 files changed, 285 insertions(+), 50 deletions(-) create mode 100644 tests/ui/panic_in_result_fn_assertions.rs create mode 100644 tests/ui/panic_in_result_fn_assertions.stderr create mode 100644 tests/ui/panic_in_result_fn_debug_assertions.rs create mode 100644 tests/ui/panic_in_result_fn_debug_assertions.stderr diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 72dfccc1089..cdabb0d0dd6 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,18 +1,16 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then}; use rustc_hir as hir; -use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::Expr; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. /// - /// **Known problems:** None. + /// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked. /// /// **Example:** /// @@ -22,9 +20,15 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` + /// Use instead: + /// ```rust + /// fn result_without_panic() -> Result { + /// Err(String::from("error")) + /// } + /// ``` pub PANIC_IN_RESULT_FN, restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion" } declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); @@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } } -struct FindPanicUnimplementedUnreachable { - result: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if ["unimplemented", "unreachable", "panic", "todo"] - .iter() - .any(|fun| is_expn_of(expr.span, fun).is_some()) - { - self.result.push(expr.span); - } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; - panics.visit_expr(&body.value); - if !panics.result.is_empty() { + let panics = find_macro_calls( + vec![ + "unimplemented", + "unreachable", + "panic", + "todo", + "assert", + "assert_eq", + "assert_ne", + "debug_assert", + "debug_assert_eq", + "debug_assert_ne", + ], + body, + ); + if !panics.is_empty() { span_lint_and_then( cx, PANIC_IN_RESULT_FN, impl_span, - "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`", move |diag| { diag.help( - "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", ); - diag.span_note(panics.result, "return Err() instead of panicking"); + diag.span_note(panics, "return Err() instead of panicking"); }, ); } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 007e72d129f..e47d71aac99 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -41,7 +41,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, @@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { visitor.found } +struct FindMacroCalls<'a> { + names: Vec<&'a str>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) { + self.result.push(expr.span); + } + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Finds calls of the specified macros in a function body. +pub fn find_macro_calls(names: Vec<&str>, body: &'tcx Body<'tcx>) -> Vec { + let mut fmc = FindMacroCalls { + names, + result: Vec::new(), + }; + fmc.visit_expr(&body.value); + fmc.result +} + /// Converts a span to a code snippet if available, otherwise use default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index ca73ac5a411..eb744b0c198 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,4 +1,4 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:7:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:9:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:12:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:14:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:17:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:19:9 | @@ -50,7 +50,7 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:22:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,7 +59,7 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:24:9 | @@ -67,7 +67,7 @@ LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:53:1 | LL | / fn function_result_with_panic() -> Result // should emit lint @@ -76,7 +76,7 @@ LL | | panic!("error"); LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:55:5 | @@ -84,7 +84,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:68:1 | LL | / fn main() -> Result<(), String> { @@ -93,7 +93,7 @@ LL | | Ok(()) LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:69:5 | diff --git a/tests/ui/panic_in_result_fn_assertions.rs b/tests/ui/panic_in_result_fn_assertions.rs new file mode 100644 index 00000000000..ffdf8288adc --- /dev/null +++ b/tests/ui/panic_in_result_fn_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_assert_with_message(x: i32) -> Result // should emit lint + { + assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_assert_eq(x: i32) -> Result // should emit lint + { + assert_eq!(x, 5); + Ok(true) + } + + fn result_with_assert_ne(x: i32) -> Result // should emit lint + { + assert_ne!(x, 1); + Ok(true) + } + + fn other_with_assert_with_message(x: i32) // should not emit lint + { + assert!(x == 5, "wrong argument"); + } + + fn other_with_assert_eq(x: i32) // should not emit lint + { + assert_eq!(x, 5); + } + + fn other_with_assert_ne(x: i32) // should not emit lint + { + assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let assert = "assert!"; + println!("No {}", assert); + Ok(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr new file mode 100644 index 00000000000..a17f043737d --- /dev/null +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:7:5 + | +LL | / fn result_with_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:9:9 + | +LL | assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:13:5 + | +LL | / fn result_with_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:15:9 + | +LL | assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:19:5 + | +LL | / fn result_with_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:21:9 + | +LL | assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs new file mode 100644 index 00000000000..b60c79f97c8 --- /dev/null +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint + { + debug_assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint + { + debug_assert_eq!(x, 5); + Ok(true) + } + + fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint + { + debug_assert_ne!(x, 1); + Ok(true) + } + + fn other_with_debug_assert_with_message(x: i32) // should not emit lint + { + debug_assert!(x == 5, "wrong argument"); + } + + fn other_with_debug_assert_eq(x: i32) // should not emit lint + { + debug_assert_eq!(x, 5); + } + + fn other_with_debug_assert_ne(x: i32) // should not emit lint + { + debug_assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let debug_assert = "debug_assert!"; + println!("No {}", debug_assert); + Ok(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result_fn_debug_assertions.stderr b/tests/ui/panic_in_result_fn_debug_assertions.stderr new file mode 100644 index 00000000000..ec18e89698c --- /dev/null +++ b/tests/ui/panic_in_result_fn_debug_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 + | +LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 + | +LL | debug_assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5 + | +LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9 + | +LL | debug_assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5 + | +LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9 + | +LL | debug_assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + From bdad7900f4558672ca02b24a0a079b05391acaf9 Mon Sep 17 00:00:00 2001 From: dp304 <34493835+dp304@users.noreply.github.com> Date: Thu, 3 Dec 2020 23:07:24 +0100 Subject: [PATCH 1060/1110] Apply suggestions from code review Use array slice instead of `Vec` in `find_macro_calls` as suggested by @ebroto Co-authored-by: Eduardo Broto --- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/utils/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index cdabb0d0dd6..37e2b50def1 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let panics = find_macro_calls( - vec![ + &[ "unimplemented", "unreachable", "panic", diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e47d71aac99..e83371f8b99 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -603,12 +603,12 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { visitor.found } -struct FindMacroCalls<'a> { - names: Vec<&'a str>, +struct FindMacroCalls<'a, 'b> { + names: &'a [&'b str], result: Vec, } -impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { +impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { @@ -625,7 +625,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { } /// Finds calls of the specified macros in a function body. -pub fn find_macro_calls(names: Vec<&str>, body: &'tcx Body<'tcx>) -> Vec { +pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec { let mut fmc = FindMacroCalls { names, result: Vec::new(), From 16d0e569248675da54fd892b4f36fd0ebc88eb1b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 8 Dec 2020 00:14:05 +0100 Subject: [PATCH 1061/1110] Update reference file --- tests/ui/panic_in_result_fn_assertions.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr index a17f043737d..86f61ad718a 100644 --- a/tests/ui/panic_in_result_fn_assertions.stderr +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -14,7 +14,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:9:9 | LL | assert!(x == 5, "wrong argument"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` From 3187cad8ec0e9c32272341028526d6f1e208a704 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 8 Dec 2020 23:17:12 +0100 Subject: [PATCH 1062/1110] Factor out some code in write.rs Get rid of the too-many-lines error. --- clippy_lints/src/write.rs | 112 ++++++++++++--------------- tests/ui/println_empty_string.fixed | 7 ++ tests/ui/println_empty_string.rs | 7 ++ tests/ui/println_empty_string.stderr | 14 +++- 4 files changed, 78 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0fd56f78572..337f7a229b9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -262,71 +262,22 @@ impl EarlyLintPass for Write { .map_or(false, |crate_name| crate_name == "build_script_build") } - if mac.path == sym!(println) { - if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == Symbol::intern("") { - span_lint_and_sugg( - cx, - PRINTLN_EMPTY_STRING, - mac.span(), - "using `println!(\"\")`", - "replace it with", - "println!()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } else if mac.path == sym!(eprintln) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); - } else if mac.path == sym!(eprint) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `eprint!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `eprintln!` instead", - vec![ - (mac.path.span, String::from("eprintln")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } - } - } else if mac.path == sym!(print) { + if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `print!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `println!` instead", - vec![ - (mac.path.span, String::from("println")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(println) { + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } + self.lint_println_empty_string(cx, mac); + } else if mac.path == sym!(eprint) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { @@ -530,6 +481,45 @@ impl Write { } } } + + fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if fmt_str.symbol == Symbol::intern("") { + let name = mac.path.segments[0].ident.name; + span_lint_and_sugg( + cx, + PRINTLN_EMPTY_STRING, + mac.span(), + &format!("using `{}!(\"\")`", name), + "replace it with", + format!("{}!()", name), + Applicability::MachineApplicable, + ); + } + } + } + + fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + let name = mac.path.segments[0].ident.name; + let suggested = format!("{}ln", name); + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + &format!("using `{}!()` with a format string that ends in a single newline", name), + |err| { + err.multipart_suggestion( + &format!("use `{}!` instead", suggested), + vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + Applicability::MachineApplicable, + ); + }, + ); + } + } + } } /// Checks if the format string contains a single newline that terminates it. diff --git a/tests/ui/println_empty_string.fixed b/tests/ui/println_empty_string.fixed index 2b889b62ea9..9760680927a 100644 --- a/tests/ui/println_empty_string.fixed +++ b/tests/ui/println_empty_string.fixed @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(), } + + eprintln!(); + eprintln!(); + + match "a" { + _ => eprintln!(), + } } diff --git a/tests/ui/println_empty_string.rs b/tests/ui/println_empty_string.rs index 890f5f68476..80fdb3e6e21 100644 --- a/tests/ui/println_empty_string.rs +++ b/tests/ui/println_empty_string.rs @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(""), } + + eprintln!(); + eprintln!(""); + + match "a" { + _ => eprintln!(""), + } } diff --git a/tests/ui/println_empty_string.stderr b/tests/ui/println_empty_string.stderr index 23112b88168..17fe4ea7479 100644 --- a/tests/ui/println_empty_string.stderr +++ b/tests/ui/println_empty_string.stderr @@ -12,5 +12,17 @@ error: using `println!("")` LL | _ => println!(""), | ^^^^^^^^^^^^ help: replace it with: `println!()` -error: aborting due to 2 previous errors +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:13:5 + | +LL | eprintln!(""); + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:16:14 + | +LL | _ => eprintln!(""), + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: aborting due to 4 previous errors From f77f1db35b1f263286962cfa5e5c08a55add83cb Mon Sep 17 00:00:00 2001 From: Korrat Date: Sat, 24 Oct 2020 16:48:10 +0200 Subject: [PATCH 1063/1110] Add a lint for maps with zero-sized values Co-authored-by: Eduardo Broto --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/zero_sized_map_values.rs | 82 ++++++++++++++++ tests/ui/zero_sized_btreemap_values.rs | 68 +++++++++++++ tests/ui/zero_sized_btreemap_values.stderr | 107 +++++++++++++++++++++ tests/ui/zero_sized_hashmap_values.rs | 68 +++++++++++++ tests/ui/zero_sized_hashmap_values.stderr | 107 +++++++++++++++++++++ 7 files changed, 437 insertions(+) create mode 100644 clippy_lints/src/zero_sized_map_values.rs create mode 100644 tests/ui/zero_sized_btreemap_values.rs create mode 100644 tests/ui/zero_sized_btreemap_values.stderr create mode 100644 tests/ui/zero_sized_hashmap_values.rs create mode 100644 tests/ui/zero_sized_hashmap_values.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 82f2ad7ec4e..af3b1c1db2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2171,5 +2171,6 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a92ae9ed8d9..5fca088c9b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -345,6 +345,7 @@ mod wildcard_dependencies; mod wildcard_imports; mod write; mod zero_div_zero; +mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; @@ -944,6 +945,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &write::WRITE_LITERAL, &write::WRITE_WITH_NEWLINE, &zero_div_zero::ZERO_DIVIDED_BY_ZERO, + &zero_sized_map_values::ZERO_SIZED_MAP_VALUES, ]); // end register lints, do not remove this comment, it’s used in `update_lints` @@ -1204,6 +1206,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); + store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1336,6 +1339,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_self::UNUSED_SELF), LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), + LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), ]); #[cfg(feature = "internal-lints")] diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs new file mode 100644 index 00000000000..1d5fa8d06a9 --- /dev/null +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -0,0 +1,82 @@ +use if_chain::if_chain; +use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Adt, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_target::abi::LayoutOf as _; +use rustc_typeck::hir_ty_to_ty; + +use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. + /// + /// **Why is this bad?** Since there is only a single value for a zero-sized type, a map + /// containing zero sized values is effectively a set. Using a set in that case improves + /// readability and communicates intent more clearly. + /// + /// **Known problems:** + /// * A zero-sized type cannot be recovered later if it contains private fields. + /// * This lints the signature of public items + /// + /// **Example:** + /// + /// ```rust + /// # use std::collections::HashMap; + /// fn unique_words(text: &str) -> HashMap<&str, ()> { + /// todo!(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::collections::HashSet; + /// fn unique_words(text: &str) -> HashSet<&str> { + /// todo!(); + /// } + /// ``` + pub ZERO_SIZED_MAP_VALUES, + pedantic, + "usage of map with zero-sized value type" +} + +declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); + +impl LateLintPass<'_> for ZeroSizedMapValues { + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + if_chain! { + if !hir_ty.span.from_expansion(); + if !in_trait_impl(cx, hir_ty.hir_id); + let ty = ty_from_hir_ty(cx, hir_ty); + if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); + if let Adt(_, ref substs) = ty.kind(); + let ty = substs.type_at(1); + if let Ok(layout) = cx.layout_of(ty); + if layout.is_zst(); + then { + span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead"); + } + } + } +} + +fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let parent_id = cx.tcx.hir().get_parent_item(hir_id); + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(parent_id)) { + if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return true; + } + } + false +} + +fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { + cx.maybe_typeck_results() + .and_then(|results| { + if results.hir_owner == hir_ty.hir_id.owner { + results.node_type_opt(hir_ty.hir_id) + } else { + None + } + }) + .unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty)) +} diff --git a/tests/ui/zero_sized_btreemap_values.rs b/tests/ui/zero_sized_btreemap_values.rs new file mode 100644 index 00000000000..5cd254787d8 --- /dev/null +++ b/tests/ui/zero_sized_btreemap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::BTreeMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = BTreeMap; +type NotOkMap = BTreeMap; + +enum TestEnum { + Ok(BTreeMap), + NotOk(BTreeMap), +} + +struct Test { + ok: BTreeMap, + not_ok: BTreeMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: BTreeMap); +} + +impl Test { + fn ok(&self) -> BTreeMap { + todo!() + } + + fn not_ok(&self) -> BTreeMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = BTreeMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: BTreeMap) { + todo!(); + } +} + +fn test(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn test2(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn main() { + let _: BTreeMap = BTreeMap::new(); + let _: BTreeMap = BTreeMap::new(); + + let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/tests/ui/zero_sized_btreemap_values.stderr b/tests/ui/zero_sized_btreemap_values.stderr new file mode 100644 index 00000000000..334d921a9af --- /dev/null +++ b/tests/ui/zero_sized_btreemap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:11:17 + | +LL | type NotOkMap = BTreeMap; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:15:11 + | +LL | NotOk(BTreeMap), + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:20:13 + | +LL | not_ok: BTreeMap, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:30:30 + | +LL | fn weird_map(&self, map: BTreeMap); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:38:25 + | +LL | fn not_ok(&self) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:14 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:50 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:35 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:12 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:67:12 + | +LL | let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + diff --git a/tests/ui/zero_sized_hashmap_values.rs b/tests/ui/zero_sized_hashmap_values.rs new file mode 100644 index 00000000000..a1608d863fb --- /dev/null +++ b/tests/ui/zero_sized_hashmap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::HashMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = HashMap; +type NotOkMap = HashMap; + +enum TestEnum { + Ok(HashMap), + NotOk(HashMap), +} + +struct Test { + ok: HashMap, + not_ok: HashMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: HashMap); +} + +impl Test { + fn ok(&self) -> HashMap { + todo!() + } + + fn not_ok(&self) -> HashMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = HashMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: HashMap) { + todo!(); + } +} + +fn test(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn test2(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn main() { + let _: HashMap = HashMap::new(); + let _: HashMap = HashMap::new(); + + let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/tests/ui/zero_sized_hashmap_values.stderr b/tests/ui/zero_sized_hashmap_values.stderr new file mode 100644 index 00000000000..43987b3d01d --- /dev/null +++ b/tests/ui/zero_sized_hashmap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:11:17 + | +LL | type NotOkMap = HashMap; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:15:11 + | +LL | NotOk(HashMap), + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:20:13 + | +LL | not_ok: HashMap, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:30:30 + | +LL | fn weird_map(&self, map: HashMap); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:38:25 + | +LL | fn not_ok(&self) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:14 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:49 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:34 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:12 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:67:12 + | +LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + From 613333acd537dff5844cd1ed72d4e6f56752ee6a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 29 Nov 2020 18:08:47 +0100 Subject: [PATCH 1064/1110] Pin Clippy to a nightly --- .github/workflows/clippy.yml | 3 --- .github/workflows/clippy_bors.yml | 11 ---------- CONTRIBUTING.md | 21 ++++++++++-------- doc/basics.md | 28 +++--------------------- rust-toolchain | 4 +++- setup-toolchain.sh | 36 ------------------------------- 6 files changed, 18 insertions(+), 85 deletions(-) delete mode 100755 setup-toolchain.sh diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index cf4aa39e49b..181b3bab421 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -56,9 +56,6 @@ jobs: restore-keys: | ${{ runner.os }}-x86_64-unknown-linux-gnu - - name: Master Toolchain Setup - run: bash setup-toolchain.sh - # Run - name: Set LD_LIBRARY_PATH (Linux) run: | diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 784463fe0df..f08182365fc 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -105,11 +105,6 @@ jobs: restore-keys: | ${{ runner.os }}-${{ matrix.host }} - - name: Master Toolchain Setup - run: bash setup-toolchain.sh - env: - HOST_TOOLCHAIN: ${{ matrix.host }} - # Run - name: Set LD_LIBRARY_PATH (Linux) if: runner.os == 'Linux' @@ -192,9 +187,6 @@ jobs: restore-keys: | ${{ runner.os }}-x86_64-unknown-linux-gnu - - name: Master Toolchain Setup - run: bash setup-toolchain.sh - # Run - name: Build Integration Test run: cargo test --test integration --features integration --no-run @@ -273,9 +265,6 @@ jobs: restore-keys: | ${{ runner.os }}-x86_64-unknown-linux-gnu - - name: Master Toolchain Setup - run: bash setup-toolchain.sh - # Download - name: Download target dir uses: actions/download-artifact@v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8c26e2d456..29cbe2a08ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -182,18 +182,21 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -## Fixing build failures caused by Rust +## Syncing changes from [`rust-lang/rust`] to Clippy -Clippy currently gets built with `rustc` of the `rust-lang/rust` `master` -branch. Most of the times we have to adapt to the changes and only very rarely -there's an actual bug in Rust. +Clippy currently gets built with a pinned nightly version. -If you decide to make Clippy work again with a Rust commit that breaks it, you -have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of -Clippy in the `rust-lang/rust` repository. +In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy +that compiler hackers modify from time to time to adapt to changes in the unstable +API of the compiler. -For general information about `subtree`s in the Rust repository see [Rust's -`CONTRIBUTING.md`][subtree]. +We need to sync these changes back to this repository periodically. If you want +to help with that, you have to sync the `rust-lang/rust-clippy` repository with +the `subtree` copy of Clippy in the `rust-lang/rust` repository, and update +the `rustc-toolchain` file accordingly. + +For general information about `subtree`s in the Rust repository +see [Rust's `CONTRIBUTING.md`][subtree]. ### Patching git-subtree to work with big repos diff --git a/doc/basics.md b/doc/basics.md index f25edb793e2..8b2a8a23890 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -1,16 +1,14 @@ # Basics for hacking on Clippy This document explains the basics for hacking on Clippy. Besides others, this -includes how to set-up the development environment, how to build and how to test -Clippy. For a more in depth description on the codebase take a look at [Adding -Lints] or [Common Tools]. +includes how to build and test Clippy. For a more in depth description on +the codebase take a look at [Adding Lints] or [Common Tools]. [Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md - [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) - [Get the code](#get-the-code) - - [Setup](#setup) - [Building and Testing](#building-and-testing) - [`cargo dev`](#cargo-dev) - [PR](#pr) @@ -38,29 +36,9 @@ git rebase upstream/master git push ``` -## Setup - -Next we need to setup the toolchain to compile Clippy. Since Clippy heavily -relies on compiler internals it is build with the latest rustc master. To get -this toolchain, you can just use the `setup-toolchain.sh` script or use -`rustup-toolchain-install-master`: - -```bash -bash setup-toolchain.sh -# OR -cargo install rustup-toolchain-install-master -# For better IDE integration also add `-c rustfmt -c rust-src` (optional) -rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools -rustup override set master -``` - -_Note:_ Sometimes you may get compiler errors when building Clippy, even if you -didn't change anything. Normally those will be fixed by a maintainer in a few hours. - ## Building and Testing -Once the `master` toolchain is installed, you can build and test Clippy like -every other Rust project: +You can build and test Clippy like every other Rust project: ```bash cargo build # builds Clippy diff --git a/rust-toolchain b/rust-toolchain index bf867e0ae5b..dba3e57bcf7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1,3 @@ -nightly +[toolchain] +channel = "nightly-2020-11-29" +components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/setup-toolchain.sh b/setup-toolchain.sh deleted file mode 100755 index 191ea4315a6..00000000000 --- a/setup-toolchain.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# Set up the appropriate rustc toolchain - -set -e - -cd "$(dirname "$0")" - -RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false -CARGO_HOME=${CARGO_HOME:-$HOME/.cargo} - -# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin -if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then - cargo +nightly install rustup-toolchain-install-master -else - VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*") - REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*") - echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH" - echo " current version : $VERSION" - echo " remote version : $REMOTE" -fi - -RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}') - -if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then - echo "info: master toolchain is up-to-date" - exit 0 -fi - -if [[ -n "$HOST_TOOLCHAIN" ]]; then - TOOLCHAIN=('--host' "$HOST_TOOLCHAIN") -else - TOOLCHAIN=() -fi - -rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -c llvm-tools -- "$RUST_COMMIT" -rustup override set master From 2e8b00a33150eebf2d1a5aa500466c20cac0c0c3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 9 Dec 2020 17:20:38 +0100 Subject: [PATCH 1065/1110] Apply suggestions from PR review Also: - Update to latest nightly --- CONTRIBUTING.md | 40 ++++++++++++++++++++-------------------- rust-toolchain | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29cbe2a08ec..0a3c602b9e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,8 +70,8 @@ But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. [`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. -Note that [`E-medium`] issues may require some knowledge of Clippy internals or some -debugging to find the actual problem behind the issue. +Note that [`E-medium`] issues may require some knowledge of Clippy internals or some +debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of @@ -182,7 +182,7 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -## Syncing changes from [`rust-lang/rust`] to Clippy +## Syncing changes between Clippy and [`rust-lang/rust`] Clippy currently gets built with a pinned nightly version. @@ -190,13 +190,18 @@ In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clipp that compiler hackers modify from time to time to adapt to changes in the unstable API of the compiler. -We need to sync these changes back to this repository periodically. If you want -to help with that, you have to sync the `rust-lang/rust-clippy` repository with -the `subtree` copy of Clippy in the `rust-lang/rust` repository, and update -the `rustc-toolchain` file accordingly. +We need to sync these changes back to this repository periodically, and the changes +made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository. -For general information about `subtree`s in the Rust repository -see [Rust's `CONTRIBUTING.md`][subtree]. +To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done +in a bi-weekly basis if there's no urgent changes. This is done starting on the day of +the Rust stable release and then every other week. That way we guarantee that we keep +this repo up to date with the latest compiler API, and every feature in Clippy is available +for 2 weeks in nightly, before it can get to beta. For reference, the first sync +following this cadence was performed the 2020-08-27. + +This process is described in detail in the following sections. For general information +about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. ### Patching git-subtree to work with big repos @@ -225,13 +230,14 @@ This shell has a hardcoded recursion limit set to 1000. In order to make this pr you need to force the script to run `bash` instead. You can do this by editing the first line of the `git-subtree` script and changing `sh` to `bash`. -### Performing the sync +### Performing the sync from [`rust-lang/rust`] to Clippy Here is a TL;DR version of the sync process (all of the following commands have to be run inside the `rust` directory): -1. Clone the [`rust-lang/rust`] repository -2. Sync the changes to the rust-copy of Clippy to your Clippy fork: +1. Clone the [`rust-lang/rust`] repository or make sure it is up to date. +2. Checkout the commit from the latest available nightly. You can get it using `rustup check`. +3. Sync the changes to the rust-copy of Clippy to your Clippy fork: ```bash # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust @@ -249,17 +255,11 @@ to be run inside the `rust` directory): git checkout sync-from-rust git merge upstream/master ``` -3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to +4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Zulip] stream.) - -### Syncing back changes in Clippy to [`rust-lang/rust`] -To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back -in a bi-weekly basis if there's no urgent changes. This is done starting on the day of -the Rust stable release and then every other week. That way we guarantee that -every feature in Clippy is available for 2 weeks in nightly, before it can get to beta. -For reference, the first sync following this cadence was performed the 2020-08-27. +### Performing the sync from Clippy to [`rust-lang/rust`] All of the following commands have to be run inside the `rust` directory. diff --git a/rust-toolchain b/rust-toolchain index dba3e57bcf7..dfa7dea449a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2020-11-29" +channel = "nightly-2020-12-09" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 77a32ebe1ead9a85ca7f0c20eb59c5b9ba8e64b4 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 9 Dec 2020 23:38:15 +0100 Subject: [PATCH 1066/1110] Use new cache key --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 181b3bab421..abdeb96d36d 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -52,7 +52,7 @@ jobs: uses: actions/cache@v2 with: path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-v2-${{ hashFiles('Cargo.lock') }} restore-keys: | ${{ runner.os }}-x86_64-unknown-linux-gnu diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f08182365fc..c86cd32951d 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -101,7 +101,7 @@ jobs: uses: actions/cache@v2 with: path: ~/.cargo - key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-${{ matrix.host }}-v2-${{ hashFiles('Cargo.lock') }} restore-keys: | ${{ runner.os }}-${{ matrix.host }} @@ -183,7 +183,7 @@ jobs: uses: actions/cache@v2 with: path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-v2-${{ hashFiles('Cargo.lock') }} restore-keys: | ${{ runner.os }}-x86_64-unknown-linux-gnu @@ -261,7 +261,7 @@ jobs: uses: actions/cache@v2 with: path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-v2-${{ hashFiles('Cargo.lock') }} restore-keys: | ${{ runner.os }}-x86_64-unknown-linux-gnu From 20d84fdd98c16e36d0701c2b5a7f52268c54977b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Dec 2020 08:51:27 +0100 Subject: [PATCH 1067/1110] Enable internal lints for every test in CI --- .github/workflows/clippy.yml | 6 +++--- .github/workflows/clippy_bors.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index abdeb96d36d..85ca63ef8c2 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -63,13 +63,13 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings + run: cargo build --features deny-warnings,internal-lints - name: Test - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings,internal-lints - name: Test clippy_lints - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings,internal-lints working-directory: clippy_lints - name: Test rustc_tools_util diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index c86cd32951d..3e3495c25f5 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -123,13 +123,13 @@ jobs: SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - - name: Build with internal lints + - name: Build run: cargo build --features deny-warnings,internal-lints - - name: Test with internal lints + - name: Test run: cargo test --features deny-warnings,internal-lints - - name: Test clippy_lints with internal lints + - name: Test clippy_lints run: cargo test --features deny-warnings,internal-lints working-directory: clippy_lints From 41cab83fdb9d10d620c0529bc837a3dddff5dd5f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Dec 2020 08:53:27 +0100 Subject: [PATCH 1068/1110] Fix toolchain installation in workflows --- .github/workflows/clippy.yml | 13 +++------- .github/workflows/clippy_bors.yml | 40 ++++++++----------------------- .github/workflows/clippy_dev.yml | 10 +++++--- 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 85ca63ef8c2..89be0e741aa 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -35,18 +35,11 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: x86_64-unknown-linux-gnu - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update + - name: Install toolchain + run: rustup show active-toolchain - name: Cache cargo dir uses: actions/cache@v2 @@ -99,5 +92,5 @@ jobs: # Cleanup - name: Run cargo-cache --autoclean run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache cargo cache diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 3e3495c25f5..af5ddc022d4 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -23,6 +23,7 @@ jobs: - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master with: github_token: "${{ secrets.github_token }}" + - name: Checkout uses: actions/checkout@v2.3.3 with: @@ -84,18 +85,11 @@ jobs: sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386 if: matrix.host == 'i686-unknown-linux-gnu' - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: ${{ matrix.host }} - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update + - name: Install toolchain + run: rustup show active-toolchain - name: Cache cargo dir uses: actions/cache@v2 @@ -153,7 +147,7 @@ jobs: # Cleanup - name: Run cargo-cache --autoclean run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache cargo cache integration_build: @@ -166,18 +160,11 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: x86_64-unknown-linux-gnu - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update + - name: Install toolchain + run: rustup show active-toolchain - name: Cache cargo dir uses: actions/cache@v2 @@ -209,7 +196,7 @@ jobs: # Cleanup - name: Run cargo-cache --autoclean run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache cargo cache integration: needs: integration_build @@ -244,18 +231,11 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: x86_64-unknown-linux-gnu - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update + - name: Install toolchain + run: rustup show active-toolchain - name: Cache cargo dir uses: actions/cache@v2 @@ -285,7 +265,7 @@ jobs: # Cleanup - name: Run cargo-cache --autoclean run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache cargo cache # These jobs doesn't actually test anything, but they're only used to tell diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 5ee157cf23b..95da775b7bc 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -22,6 +22,12 @@ jobs: steps: # Setup + - name: Checkout + uses: actions/checkout@v2.3.3 + + - name: remove toolchain file + run: rm rust-toolchain + - name: rust-toolchain uses: actions-rs/toolchain@v1.0.6 with: @@ -29,9 +35,7 @@ jobs: target: x86_64-unknown-linux-gnu profile: minimal components: rustfmt - - - name: Checkout - uses: actions/checkout@v2.3.3 + default: true # Run - name: Build From 26dcbf5523fb95c2490862af37c7279244f9a912 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Dec 2020 09:36:19 +0100 Subject: [PATCH 1069/1110] Stop caching on CI The only thing we now cache is cargo-cache, which we only use for cache. That's a catch-22 if I ever seen one. And for Clippy itself we always want to do a clean build and not cache anything. --- .github/workflows/clippy.yml | 14 ----------- .github/workflows/clippy_bors.yml | 41 ------------------------------- 2 files changed, 55 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 89be0e741aa..530e60001f7 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -41,14 +41,6 @@ jobs: - name: Install toolchain run: rustup show active-toolchain - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-v2-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-x86_64-unknown-linux-gnu - # Run - name: Set LD_LIBRARY_PATH (Linux) run: | @@ -88,9 +80,3 @@ jobs: cargo dev new_lint --name new_late_pass --pass late cargo check git reset --hard HEAD - - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index af5ddc022d4..ae31534b71c 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -91,14 +91,6 @@ jobs: - name: Install toolchain run: rustup show active-toolchain - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-${{ matrix.host }}-v2-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.host }} - # Run - name: Set LD_LIBRARY_PATH (Linux) if: runner.os == 'Linux' @@ -144,12 +136,6 @@ jobs: env: OS: ${{ runner.os }} - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache - integration_build: needs: changelog runs-on: ubuntu-latest @@ -166,14 +152,6 @@ jobs: - name: Install toolchain run: rustup show active-toolchain - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-v2-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-x86_64-unknown-linux-gnu - # Run - name: Build Integration Test run: cargo test --test integration --features integration --no-run @@ -193,11 +171,6 @@ jobs: name: target path: target - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache integration: needs: integration_build strategy: @@ -237,14 +210,6 @@ jobs: - name: Install toolchain run: rustup show active-toolchain - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-v2-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-x86_64-unknown-linux-gnu - # Download - name: Download target dir uses: actions/download-artifact@v1 @@ -262,12 +227,6 @@ jobs: INTEGRATION: ${{ matrix.integration }} RUSTUP_TOOLCHAIN: master - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache - # These jobs doesn't actually test anything, but they're only used to tell # bors the build completed, as there is no practical way to detect when a # workflow is successful listening to webhooks only. From 3f41fe2704e331399568890f41874c2fc086e838 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Dec 2020 10:44:33 +0100 Subject: [PATCH 1070/1110] Error in integration test, if required toolchain is not installed --- tests/integration.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration.rs b/tests/integration.rs index a78273ce0da..1718089e8bd 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -72,6 +72,8 @@ fn integration_test() { panic!("incompatible crate versions"); } else if stderr.contains("failed to run `rustc` to learn about target-specific information") { panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`"); + } else if stderr.contains("toolchain") && stderr.contains("is not installed") { + panic!("missing required toolchain"); } match output.status.code() { From 836325e9d9530114b296f73306c03318d8a52c0a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Dec 2020 11:00:05 +0100 Subject: [PATCH 1071/1110] Fix integration test runner --- .github/workflows/clippy_bors.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index ae31534b71c..5d846eb64c7 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -222,10 +222,11 @@ jobs: # Run - name: Test ${{ matrix.integration }} - run: $CARGO_TARGET_DIR/debug/integration + run: | + RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \ + $CARGO_TARGET_DIR/debug/integration env: INTEGRATION: ${{ matrix.integration }} - RUSTUP_TOOLCHAIN: master # These jobs doesn't actually test anything, but they're only used to tell # bors the build completed, as there is no practical way to detect when a From a6bb9276f7964b96899d04d680bc04bf99c8bf47 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 9 Nov 2020 23:38:08 +0100 Subject: [PATCH 1072/1110] Lint wrong self convention in trait also --- clippy_lints/src/methods/mod.rs | 49 +++++++++++++++++++++++---- tests/ui/wrong_self_convention.rs | 26 ++++++++++++++ tests/ui/wrong_self_convention.stderr | 34 ++++++++++++++++++- 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8002c27a5e9..3c89c1b6ed2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -22,6 +22,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; @@ -1623,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let def_id = cx.tcx.hir().local_def_id(item.hir_id); let self_ty = cx.tcx.type_of(def_id); + + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); - if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind; let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); let method_sig = cx.tcx.fn_sig(method_def_id); @@ -1697,11 +1703,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { - return; - } - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1735,8 +1736,42 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + if_chain! { + if let TraitItemKind::Fn(ref sig, _) = item.kind; + if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + let first_arg_span = first_arg_ty.span; + let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + + then { + if let Some((ref conv, self_kinds)) = &CONVENTIONS + .iter() + .find(|(ref conv, _)| conv.check(&item.ident.name.as_str())) + { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + WRONG_PUB_SELF_CONVENTION, + first_arg_span, + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } + } + } + if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index f44305d7e48..275866b8248 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -88,3 +88,29 @@ mod issue4037 { } } } + +// Lint also in trait definition (see #6307) +mod issue6307{ + trait T: Sized { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(&self) {} + fn into_u32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn to_u32(&self) {} + fn from_i32(self) {} + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_pub_self_convention)] + fn from_cake(self) {} + + // test for false positives + fn as_(self) {} + fn into_(&self) {} + fn is_(self) {} + fn to_(self) {} + fn from_(self) {} + fn to_mut(&mut self) {} + } +} \ No newline at end of file diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index ef3ad73ebc7..64aa957fed6 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -72,5 +72,37 @@ error: methods called `from_*` usually take no self; consider choosing a less am LL | pub fn from_i64(self) {} | ^^^^ -error: aborting due to 12 previous errors +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:95:19 + | +LL | fn as_i32(self) {} + | ^^^^ + | + = note: `-D clippy::wrong-pub-self-convention` implied by `-D warnings` + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:97:21 + | +LL | fn into_i32(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:99:19 + | +LL | fn is_i32(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:101:19 + | +LL | fn to_i32(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:103:21 + | +LL | fn from_i32(self) {} + | ^^^^ + +error: aborting due to 17 previous errors From 4af9382bec3f49d9c7e0f03e9cfb5e8f1c70fdbe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 10 Nov 2020 08:56:17 +0100 Subject: [PATCH 1073/1110] Common function to lint wrong self convention from impl and trait def --- clippy_lints/src/methods/mod.rs | 88 +++++++++++++-------------- tests/ui/wrong_self_convention.rs | 6 +- tests/ui/wrong_self_convention.stderr | 2 - 3 files changed, 45 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c89c1b6ed2..9a082a89229 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1674,32 +1674,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&name)) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - let lint = if item.vis.node.is_pub() { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; - - span_lint( - cx, - lint, - first_arg.pat.span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span + ); } } @@ -1748,26 +1730,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); then { - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&item.ident.name.as_str())) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( - cx, - WRONG_PUB_SELF_CONVENTION, - first_arg_span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span); } } @@ -1792,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } +fn lint_wrong_self_convention<'tcx>( + cx: &LateContext<'tcx>, + item_name: &str, + is_pub: bool, + self_ty: &'tcx TyS<'tcx>, + first_arg_ty: &'tcx TyS<'tcx>, + first_arg_span: Span, +) { + let lint = if is_pub { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + lint, + first_arg_span, + &format!( + "methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } +} + /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 275866b8248..795ba77274c 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -90,7 +90,7 @@ mod issue4037 { } // Lint also in trait definition (see #6307) -mod issue6307{ +mod issue6307 { trait T: Sized { fn as_i32(self) {} fn as_u32(&self) {} @@ -102,7 +102,7 @@ mod issue6307{ fn to_u32(&self) {} fn from_i32(self) {} // check whether the lint can be allowed at the function level - #[allow(clippy::wrong_pub_self_convention)] + #[allow(clippy::wrong_self_convention)] fn from_cake(self) {} // test for false positives @@ -113,4 +113,4 @@ mod issue6307{ fn from_(self) {} fn to_mut(&mut self) {} } -} \ No newline at end of file +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 64aa957fed6..289da6f059e 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -77,8 +77,6 @@ error: methods called `as_*` usually take self by reference or self by mutable r | LL | fn as_i32(self) {} | ^^^^ - | - = note: `-D clippy::wrong-pub-self-convention` implied by `-D warnings` error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name --> $DIR/wrong_self_convention.rs:97:21 From db98651e722ca1cc12f2ffe159c1bc128880f8a4 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 10 Nov 2020 12:26:09 +0100 Subject: [PATCH 1074/1110] Allow `wrong_self_convention` in `use_self` test for trait def --- tests/ui/use_self.fixed | 1 + tests/ui/use_self.rs | 1 + tests/ui/use_self.stderr | 38 +++++++++++++++++++------------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index ebb3aa28daf..ded3fbb56eb 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -71,6 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { + #[allow(clippy::clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 8a182192ab3..a4f7b0bfd24 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -71,6 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { + #[allow(clippy::clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index b33928597c1..80e1bfc75e8 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -37,19 +37,19 @@ LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:56 + --> $DIR/use_self.rs:90:56 | LL | fn bad(foos: &[Self]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:104:13 + --> $DIR/use_self.rs:105:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:25 + --> $DIR/use_self.rs:113:25 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -60,7 +60,7 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:17 + --> $DIR/use_self.rs:114:17 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` @@ -71,91 +71,91 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:13 + --> $DIR/use_self.rs:150:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:136:29 + --> $DIR/use_self.rs:137:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:21 + --> $DIR/use_self.rs:138:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:166:21 + --> $DIR/use_self.rs:167:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:168:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:169:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:199:13 + --> $DIR/use_self.rs:200:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:200:13 + --> $DIR/use_self.rs:201:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:202:13 + --> $DIR/use_self.rs:203:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:235:25 + --> $DIR/use_self.rs:236:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:236:13 + --> $DIR/use_self.rs:237:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:16 + --> $DIR/use_self.rs:241:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:22 + --> $DIR/use_self.rs:241:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` From 1e0f85b2640e9c2a90c197780c534a96148138bc Mon Sep 17 00:00:00 2001 From: Thibaud Date: Tue, 24 Nov 2020 18:04:58 +0100 Subject: [PATCH 1075/1110] Update tests/ui/use_self.rs Co-authored-by: Eduardo Broto --- tests/ui/use_self.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index a4f7b0bfd24..b04d9ce75b2 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -71,7 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - #[allow(clippy::clippy::wrong_self_convention)] + #[allow(clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } From 90a16e43972f8039e1f045752f04b4011a38b92f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 10 Dec 2020 17:00:55 +0100 Subject: [PATCH 1076/1110] Add tests for unsized trait in `wrong_self_convention` lint --- tests/ui/use_self.fixed | 2 +- tests/ui/wrong_self_convention.rs | 23 +++++++++++++++++++ tests/ui/wrong_self_convention.stderr | 32 ++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index ded3fbb56eb..d6a890014e6 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -71,7 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - #[allow(clippy::clippy::wrong_self_convention)] + #[allow(clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 795ba77274c..5282eba74fd 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -113,4 +113,27 @@ mod issue6307 { fn from_(self) {} fn to_mut(&mut self) {} } + + trait U { + fn as_i32(self); + fn as_u32(&self); + fn into_i32(&self); + fn into_u32(self); + fn is_i32(self); + fn is_u32(&self); + fn to_i32(self); + fn to_u32(&self); + fn from_i32(self); + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self); + + // test for false positives + fn as_(self); + fn into_(&self); + fn is_(self); + fn to_(self); + fn from_(self); + fn to_mut(&mut self); + } } diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 289da6f059e..86467eb0fc7 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -102,5 +102,35 @@ error: methods called `from_*` usually take no self; consider choosing a less am LL | fn from_i32(self) {} | ^^^^ -error: aborting due to 17 previous errors +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:118:19 + | +LL | fn as_i32(self); + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:120:21 + | +LL | fn into_i32(&self); + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:122:19 + | +LL | fn is_i32(self); + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:124:19 + | +LL | fn to_i32(self); + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:126:21 + | +LL | fn from_i32(self); + | ^^^^ + +error: aborting due to 22 previous errors From a7cfffef263a8b5a5dadfad0b7f56acd83d02b9b Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sat, 5 Dec 2020 04:59:22 -0800 Subject: [PATCH 1077/1110] add MSRV to more lints specified in #6097 update tests --- clippy_lints/src/checked_conversions.rs | 26 ++++++- clippy_lints/src/lib.rs | 15 ++-- clippy_lints/src/mem_replace.rs | 27 +++++-- clippy_lints/src/methods/mod.rs | 23 +++++- clippy_lints/src/missing_const_for_fn.rs | 29 +++++++- clippy_lints/src/ranges.rs | 29 ++++++-- clippy_lints/src/redundant_field_names.rs | 25 ++++++- .../src/redundant_static_lifetimes.rs | 26 ++++++- clippy_lints/src/use_self.rs | 25 ++++++- tests/ui/min_rust_version_attr.rs | 74 ++++++++++++++++++- tests/ui/min_rust_version_attr.stderr | 55 ++++++-------- 11 files changed, 284 insertions(+), 70 deletions(-) diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 28c1a54d2c5..54bc69e058b 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -6,9 +6,12 @@ use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; + +const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); declare_clippy_lint! { /// **What it does:** Checks for explicit bounds checking when casting. @@ -39,10 +42,25 @@ declare_clippy_lint! { "`try_from` could replace manual bounds checking when casting" } -declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); +pub struct CheckedConversions { + msrv: Option, +} + +impl CheckedConversions { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) { + return; + } + let result = if_chain! { if !in_external_macro(cx.sess(), item.span); if let ExprKind::Binary(op, ref left, ref right) = &item.kind; @@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { } } } + + extract_msrv_attr!(LateContext); } /// Searches for a single check from unsigned to _ is done diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a92ae9ed8d9..38a25d22aa2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1003,6 +1003,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); + store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); + store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); + store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); + store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box use_self::UseSelf::new(msrv)); + store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); @@ -1013,7 +1021,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box ranges::Ranges); store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); @@ -1058,7 +1065,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_multiply::NegMultiply); store.register_late_pass(|| box mem_discriminant::MemDiscriminant); store.register_late_pass(|| box mem_forget::MemForget); - store.register_late_pass(|| box mem_replace::MemReplace); store.register_late_pass(|| box arithmetic::Arithmetic::default()); store.register_late_pass(|| box assign_ops::AssignOps); store.register_late_pass(|| box let_if_seq::LetIfSeq); @@ -1080,7 +1086,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); - store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); @@ -1106,10 +1111,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); - store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); - store.register_late_pass(|| box checked_conversions::CheckedConversions); store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box inherent_to_string::InherentToString); let max_trait_bounds = conf.max_trait_bounds; @@ -1138,7 +1141,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); - store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); @@ -1178,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); - store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index bb0acecc5a9..19087b02077 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,13 +1,14 @@ use crate::utils::{ - in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help, + in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -94,7 +95,7 @@ declare_clippy_lint! { "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`" } -declare_lint_pass!(MemReplace => +impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { @@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } } +const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); + +pub struct MemReplace { + msrv: Option, +} + +impl MemReplace { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { @@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - check_replace_with_default(cx, src, dest, expr.span); + if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) { + check_replace_with_default(cx, src, dest, expr.span); + } } } } + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8002c27a5e9..5133f31e0e7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1487,7 +1487,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { - if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, @@ -1509,7 +1509,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), - ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), + ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2733,6 +2733,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s /// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( @@ -2740,7 +2742,11 @@ fn lint_map_unwrap_or_else<'tcx>( expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, ) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); @@ -2923,9 +2929,20 @@ fn lint_filter_map<'tcx>( } } +const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); + /// lint use of `filter_map().next()` for `Iterators` -fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { +fn lint_filter_map_next<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) { if match_trait_method(cx, expr, &paths::ITERATOR) { + if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + return; + } + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 38e2ce563ee..6ebeaced62a 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,14 +1,19 @@ use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; +use crate::utils::{ + fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method, +}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; +const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + declare_clippy_lint! { /// **What it does:** /// @@ -69,7 +74,18 @@ declare_clippy_lint! { "Lint functions definitions that could be made `const fn`" } -declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); +impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); + +pub struct MissingConstForFn { + msrv: Option, +} + +impl MissingConstForFn { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { fn check_fn( @@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { + if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) { + return; + } + let def_id = cx.tcx.hir().local_def_id(hir_id); if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) { @@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); } } + extract_msrv_attr!(LateContext); } /// Returns true if any of the method parameters is a type that implements `Drop`. The method diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4b514bbd42c..f9173808089 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -3,9 +3,10 @@ use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::sym; use rustc_span::symbol::Ident; @@ -13,8 +14,8 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, - span_lint, span_lint_and_sugg, span_lint_and_then, + get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -160,7 +161,20 @@ declare_clippy_lint! { "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" } -declare_lint_pass!(Ranges => [ +const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); + +pub struct Ranges { + msrv: Option, +} + +impl Ranges { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, @@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, ref l, ref r) => { - check_possible_range_contains(cx, op.node, l, r, expr.span); + if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { + check_possible_range_contains(cx, op.node, l, r, expr.span); + } }, _ => {}, } @@ -184,6 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_inclusive_range_minus_one(cx, expr); check_reversed_empty_range(cx, expr); } + extract_msrv_attr!(LateContext); } fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 2a81170e49e..38dcf7a192c 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,9 +1,12 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{meets_msrv, span_lint_and_sugg}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands @@ -33,10 +36,25 @@ declare_clippy_lint! { "checks for fields in struct literals where shorthands could be used" } -declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); +pub struct RedundantFieldNames { + msrv: Option, +} + +impl RedundantFieldNames { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) { + return; + } + if in_external_macro(cx.sess, expr.span) { return; } @@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames { } } } + extract_msrv_attr!(EarlyContext); } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 7bbcc67aa2d..fcfa3c12755 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,8 +1,11 @@ -use crate::utils::{snippet, span_lint_and_then}; +use crate::utils::{meets_msrv, snippet, span_lint_and_then}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. @@ -29,7 +32,18 @@ declare_clippy_lint! { "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them." } -declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); +pub struct RedundantStaticLifetimes { + msrv: Option, +} + +impl RedundantStaticLifetimes { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); impl RedundantStaticLifetimes { // Recursively visit types @@ -84,6 +98,10 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) { + return; + } + if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); @@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes { } } } + + extract_msrv_attr!(EarlyContext); } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 5ac4797680b..3b23f885e08 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -12,11 +12,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_middle::ty::{DefIdTree, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{differing_macro_contexts, span_lint_and_sugg}; +use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unnecessary repetition of structure name when a @@ -53,7 +54,7 @@ declare_clippy_lint! { "unnecessary structure name repetition whereas `Self` is applicable" } -declare_lint_pass!(UseSelf => [USE_SELF]); +impl_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; @@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>( } } +const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + +pub struct UseSelf { + msrv: Option, +} + +impl UseSelf { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + return; + } + if in_external_macro(cx.sess(), item.span) { return; } @@ -204,6 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } } + extract_msrv_attr!(LateContext); } struct UseSelfVisitor<'a, 'tcx> { diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 1026cc40d3b..ac75f5e46c3 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![clippy::msrv = "1.0.0"] -use std::ops::Deref; +use std::ops::{Deref, RangeFrom}; fn option_as_ref_deref() { let mut opt = Some(String::from("123")); @@ -42,12 +42,84 @@ pub fn manual_strip_msrv() { } } +pub fn redundant_fieldnames() { + let start = 0; + let _ = RangeFrom { start: start }; +} + +pub fn redundant_static_lifetime() { + const VAR_ONE: &'static str = "Test constant #1"; +} + +pub fn checked_conversion() { + let value: i64 = 42; + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +pub fn filter_map_next() { + let a = ["1", "lol", "3", "NaN", "5"]; + + #[rustfmt::skip] + let _: Option = vec![1, 2, 3, 4, 5, 6] + .into_iter() + .filter_map(|x| { + if x == 2 { + Some(x * 2) + } else { + None + } + }) + .next(); +} + +pub fn manual_range_contains() { + x >= 8 && x < 12; +} + +pub fn use_self() { + struct Foo {} + + impl Foo { + fn new() -> Foo { + Foo {} + } + fn test() -> Foo { + Foo::new() + } + } +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::replace(s, String::default()); +} + +fn map_unwrap_or() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt + .map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); +} + fn main() { + filter_map_next(); + checked_conversion(); + redundant_fieldnames(); + redundant_static_lifetime(); option_as_ref_deref(); match_like_matches(); match_same_arms(); match_same_arms2(); manual_strip_msrv(); + manual_range_contains(); + use_self(); + replace_with_default(); + map_unwrap_or(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 3e1af046e7a..d3eafe7312f 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,28 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:60:24 - | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::manual-strip` implied by `-D warnings` -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:59:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method - | -LL | if let Some() = s.strip_prefix("hello, ") { -LL | assert_eq!(.to_uppercase(), "WORLD!"); +error[E0425]: cannot find value `x` in this scope + --> $DIR/min_rust_version_attr.rs:77:5 | +LL | x >= 8 && x < 12; + | ^ not found in this scope -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:72:24 - | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:71:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method - | -LL | if let Some() = s.strip_prefix("hello, ") { -LL | assert_eq!(.to_uppercase(), "WORLD!"); +error[E0425]: cannot find value `x` in this scope + --> $DIR/min_rust_version_attr.rs:77:15 | +LL | x >= 8 && x < 12; + | ^ not found in this scope -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/min_rust_version_attr.rs:95:31 + | +LL | let _ = std::mem::replace(s, String::default()); + | ^ + | | + | expected `&mut _`, found struct `std::string::String` + | help: consider mutably borrowing here: `&mut s` + | + = note: expected mutable reference `&mut _` + found struct `std::string::String` +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`. From 8df11e431b71caa7b4c891c70e9cc48144603067 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Tue, 8 Dec 2020 22:25:20 +0530 Subject: [PATCH 1078/1110] add instructions to include msrv in lints --- doc/adding_lints.md | 57 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index b1dacfc9c6d..a723b0a4c20 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -226,13 +226,13 @@ store.register_early_pass(|| box foo_functions::FooFunctions); ``` As one may expect, there is a corresponding `register_late_pass` method -available as well. Without a call to one of `register_early_pass` or +available as well. Without a call to one of `register_early_pass` or `register_late_pass`, the lint pass in question will not be run. -One reason that `cargo dev` does not automate this step is that multiple lints +One reason that `cargo dev` does not automate this step is that multiple lints can use the same lint pass, so registering the lint pass may already be done when adding a new lint. Another reason that this step is not automated is that -the order that the passes are registered determines the order the passes +the order that the passes are registered determines the order the passes actually run, which in turn affects the order that any emitted lints are output in. @@ -380,6 +380,57 @@ pass. [`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn [ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html +## Specifying the lint's minimum supported Rust version (msrv) + +Projects supporting older versions of Rust would need to disable a lint if it targets features +present in later versions. Support for this can be added by specifying an msrv in your lint like so, + +```rust +const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); +``` + +The project's msrv will also have to be an attribute in the lint so you'll have to add a struct +and constructor for your lint. The project's msrv needs to be passed when the lint is registered +in `lib.rs` + +```rust +pub struct ManualStrip { + msrv: Option, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} +``` + +The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility +function. + +``` rust +if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + return; +} +``` + +The project's msrv can also be specified as an inner attribute, which overrides the value from +`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing +LateContext/EarlyContext. + +```rust +impl<'tcx> LateLintPass<'tcx> for ManualStrip { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + ... + } + extract_msrv_attr!(LateContext); +} +``` + +Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs` +which verifies that the lint isn't emitted if the project's msrv is lower. + ## Author lint If you have trouble implementing your lint, there is also the internal `author` From 9f27b7428307ecc6995a06f3bd666eccdbed6c99 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 9 Dec 2020 16:39:33 +0530 Subject: [PATCH 1079/1110] add test for missing_const_for_fn. fix test stderr --- tests/ui/min_rust_version_attr.rs | 12 +++++- tests/ui/min_rust_version_attr.stderr | 53 ++++++++++++++++----------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index ac75f5e46c3..3848bca3207 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -73,7 +73,11 @@ pub fn filter_map_next() { .next(); } +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] pub fn manual_range_contains() { + let x = 5; x >= 8 && x < 12; } @@ -92,7 +96,7 @@ pub fn use_self() { fn replace_with_default() { let mut s = String::from("foo"); - let _ = std::mem::replace(s, String::default()); + let _ = std::mem::replace(&mut s, String::default()); } fn map_unwrap_or() { @@ -106,6 +110,11 @@ fn map_unwrap_or() { .unwrap_or(0); } +// Could be const +fn missing_const_for_fn() -> i32 { + 1 +} + fn main() { filter_map_next(); checked_conversion(); @@ -120,6 +129,7 @@ fn main() { use_self(); replace_with_default(); map_unwrap_or(); + missing_const_for_fn(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d3eafe7312f..34805263104 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,28 +1,37 @@ -error[E0425]: cannot find value `x` in this scope - --> $DIR/min_rust_version_attr.rs:77:5 +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:142:24 | -LL | x >= 8 && x < 12; - | ^ not found in this scope - -error[E0425]: cannot find value `x` in this scope - --> $DIR/min_rust_version_attr.rs:77:15 +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ | -LL | x >= 8 && x < 12; - | ^ not found in this scope - -error[E0308]: mismatched types - --> $DIR/min_rust_version_attr.rs:95:31 + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:141:9 | -LL | let _ = std::mem::replace(s, String::default()); - | ^ - | | - | expected `&mut _`, found struct `std::string::String` - | help: consider mutably borrowing here: `&mut s` +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); | - = note: expected mutable reference `&mut _` - found struct `std::string::String` -error: aborting due to 3 previous errors +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:154:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:153:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0308, E0425. -For more information about an error, try `rustc --explain E0308`. From 26c61c7e49b173c8ae7c54c3a4c90b60cd9f71b8 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 25 Nov 2020 17:07:50 +0900 Subject: [PATCH 1080/1110] Fix FP of `manual_range_contains` in `const fn` --- clippy_lints/src/ranges.rs | 11 ++++++++--- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index f9173808089..3e454eecd97 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -14,7 +14,7 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { }, ExprKind::Binary(ref op, ref l, ref r) => { if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { - check_possible_range_contains(cx, op.node, l, r, expr.span); + check_possible_range_contains(cx, op.node, l, r, expr); } }, _ => {}, @@ -203,7 +203,12 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { + if in_constant(cx, expr.hir_id) { + return; + } + + let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 048874a7f82..47c974e614b 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -44,3 +44,8 @@ fn main() { (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 60ad259f404..835deced5e4 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -44,3 +44,8 @@ fn main() { y >= 0. && y < 1.; y < 0. || y > 1.; } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} From 0b145d688b293a92cd855000f249d83acae53f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 12 Dec 2020 01:09:30 +0100 Subject: [PATCH 1081/1110] clone_double_ref: print reference type in lint message changelog: clone_double_ref: print the type of the reference in lint message --- clippy_lints/src/methods/mod.rs | 7 +++++-- tests/ui/unnecessary_clone.stderr | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..ce234e01a1b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2100,8 +2100,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp cx, CLONE_DOUBLE_REF, expr.span, - "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + &format!( + "using `clone` on a double-reference; \ + this will copy the reference of type `{}` instead of cloning the inner type", + ty + ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..b908d0ce9c1 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -44,7 +44,7 @@ error: using `clone` on a `Copy` type LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -66,7 +66,7 @@ error: using `clone` on a `Copy` type LL | let _: E = a.clone(); | ^^^^^^^^^ help: try dereferencing it: `*****a` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:89:22 | LL | let _ = &mut encoded.clone(); @@ -81,7 +81,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &mut <&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:90:18 | LL | let _ = &encoded.clone(); From b2cb6ffbe3735ef8f137c9a6c1290c4a078793ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 12 Dec 2020 01:23:28 +0100 Subject: [PATCH 1082/1110] clone_on_copy: show the type in the lint message changelog: clone_on_copy: show the type in the lint message --- clippy_lints/src/methods/mod.rs | 16 +++++++++++----- tests/ui/clone_on_copy.stderr | 10 +++++----- tests/ui/unnecessary_clone.stderr | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..03eaee35fc4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2174,11 +2174,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp } else { snip = None; } - span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }); + span_lint_and_then( + cx, + CLONE_ON_COPY, + expr.span, + &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + |diag| { + if let Some((text, snip)) = snip { + diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); + } + }, + ); } } diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index ec2faf4ab40..14a700886a7 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,4 +1,4 @@ -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:22:5 | LL | 42.clone(); @@ -6,25 +6,25 @@ LL | 42.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:26:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:29:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` -error: using `clone` on a `Copy` type +error: using `clone` on type `char` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:35:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:39:14 | LL | vec.push(42.clone()); diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..bb2dd998f27 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -30,7 +30,7 @@ error: using `.clone()` on a ref-counted pointer LL | let _: Arc = x.clone(); | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` -error: using `clone` on a `Copy` type +error: using `clone` on type `T` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:40:5 | LL | t.clone(); @@ -38,7 +38,7 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `std::option::Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); @@ -60,7 +60,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a `Copy` type +error: using `clone` on type `many_derefs::E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); From 4bd9ed9b88d47bba3dc91fde6c0a27b63f63fe4b Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Wed, 2 Dec 2020 18:20:02 +0100 Subject: [PATCH 1083/1110] Rewrite update-all-references bash scripts in Rust This replaces the `update-all-references` scripts with a single cargo dev bless command. cc #5394 --- clippy_dev/src/bless.rs | 71 +++++++++++++++++++++++++ clippy_dev/src/lib.rs | 1 + clippy_dev/src/main.rs | 6 ++- doc/adding_lints.md | 13 +++-- doc/basics.md | 2 +- tests/ui-cargo/update-all-references.sh | 17 +----- tests/ui-cargo/update-references.sh | 46 ---------------- tests/ui-toml/update-all-references.sh | 17 +----- tests/ui-toml/update-references.sh | 46 ---------------- tests/ui/update-all-references.sh | 20 +------ tests/ui/update-references.sh | 56 ------------------- 11 files changed, 87 insertions(+), 208 deletions(-) create mode 100644 clippy_dev/src/bless.rs delete mode 100755 tests/ui-cargo/update-references.sh delete mode 100755 tests/ui-toml/update-references.sh delete mode 100755 tests/ui/update-references.sh diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs new file mode 100644 index 00000000000..45e403fa74d --- /dev/null +++ b/clippy_dev/src/bless.rs @@ -0,0 +1,71 @@ +//! `bless` updates the 'expected output' files in the repo with changed output files +//! from the last test run. + +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::lazy::SyncLazy; +use std::path::PathBuf; +use walkdir::WalkDir; + +use crate::clippy_project_root; + +// NOTE: this is duplicated with tests/cargo/mod.rs What to do? +pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), +}); + +pub fn bless() { + let test_dirs = [ + clippy_project_root().join("tests").join("ui"), + clippy_project_root().join("tests").join("ui-toml"), + clippy_project_root().join("tests").join("ui-cargo"), + ]; + for test_dir in &test_dirs { + WalkDir::new(test_dir) + .into_iter() + .filter_map(Result::ok) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .for_each(|f| { + update_test_file(f.path().with_extension("stdout")); + update_test_file(f.path().with_extension("stderr")); + update_test_file(f.path().with_extension("fixed")); + }); + } +} + +fn update_test_file(test_file_path: PathBuf) { + let build_output_path = build_dir().join(PathBuf::from(test_file_path.file_name().unwrap())); + let relative_test_file_path = test_file_path.strip_prefix(clippy_project_root()).unwrap(); + + // If compiletest did not write any changes during the test run, + // we don't have to update anything + if !build_output_path.exists() { + return; + } + + let build_output = fs::read(&build_output_path).expect("Unable to read build output file"); + let test_file = fs::read(&test_file_path).expect("Unable to read test file"); + + if build_output != test_file { + // If a test run caused an output file to change, update the test file + println!("updating {}", &relative_test_file_path.display()); + fs::copy(build_output_path, &test_file_path).expect("Could not update test file"); + + if test_file.is_empty() { + // If we copied over an empty output file, we remove it + println!("removing {}", &relative_test_file_path.display()); + fs::remove_file(test_file_path).expect("Could not remove test file"); + } + } +} + +fn build_dir() -> PathBuf { + let profile = format!("{}", env::var("PROFILE").unwrap_or("debug".to_string())); + let mut path = PathBuf::new(); + path.push(CARGO_TARGET_DIR.clone()); + path.push(profile); + path.push("test_build_base"); + path +} diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index f51c45e9eb5..17cc08ee10f 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -10,6 +10,7 @@ use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::WalkDir; +pub mod bless; pub mod fmt; pub mod new_lint; pub mod ra_setup; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 7a8cbd5251d..f66855620e7 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,10 +1,11 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; +use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; fn main() { let matches = App::new("Clippy developer tooling") + .subcommand(SubCommand::with_name("bless").about("bless the test output changes")) .subcommand( SubCommand::with_name("fmt") .about("Run rustfmt on all projects and tests") @@ -116,6 +117,9 @@ fn main() { .get_matches(); match matches.subcommand() { + ("bless", Some(_)) => { + bless::bless(); + }, ("fmt", Some(matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); }, diff --git a/doc/adding_lints.md b/doc/adding_lints.md index a723b0a4c20..60dfdb76650 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -98,12 +98,12 @@ While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want. Once we are satisfied with the output, we need to run -`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint. +`cargo dev bless` to update the `.stderr` file for our lint. Please note that, we should run `TESTNAME=foo_functions cargo uitest` -every time before running `tests/ui/update-all-references.sh`. +every time before running `cargo dev bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit our lint, we need to commit the generated `.stderr` files, too. In general, you -should only commit files changed by `tests/ui/update-all-references.sh` for the +should only commit files changed by `cargo dev bless` for the specific lint you are creating/editing. Note that if the generated files are empty, they should be removed. @@ -122,8 +122,7 @@ we will find by default two new crates, each with its manifest file: If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` -variable to `cargo uitest` works too, but the script to update the references -is in another path: `tests/ui-cargo/update-all-references.sh`. +variable to `cargo uitest` works too. ## Rustfix tests @@ -133,7 +132,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions from the lint to the code of the test file and compare that to the contents of a `.fixed` file. -Use `tests/ui/update-all-references.sh` to automatically generate the +Use `cargo dev bless` to automatically generate the `.fixed` file after running the tests. [rustfix]: https://github.com/rust-lang/rustfix @@ -368,7 +367,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { Now we should also run the full test suite with `cargo test`. At this point running `cargo test` should produce the expected output. Remember to run -`tests/ui/update-all-references.sh` to update the `.stderr` file. +`cargo dev bless` to update the `.stderr` file. `cargo test` (as opposed to `cargo uitest`) will also ensure that our lint implementation is not violating any Clippy lints itself. diff --git a/doc/basics.md b/doc/basics.md index 8b2a8a23890..dc71f022773 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -61,7 +61,7 @@ If the output of a [UI test] differs from the expected output, you can update th reference file with: ```bash -sh tests/ui/update-all-references.sh +cargo dev bless ``` For example, this is necessary, if you fix a typo in an error message of a lint diff --git a/tests/ui-cargo/update-all-references.sh b/tests/ui-cargo/update-all-references.sh index 7028b251ea0..4391499a1e1 100755 --- a/tests/ui-cargo/update-all-references.sh +++ b/tests/ui-cargo/update-all-references.sh @@ -1,18 +1,3 @@ #!/bin/bash -# -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -BUILD_DIR=$PWD/target/debug/test_build_base -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh deleted file mode 100755 index 2ab51168bca..00000000000 --- a/tests/ui-cargo/update-references.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a foo.stderr file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi -done diff --git a/tests/ui-toml/update-all-references.sh b/tests/ui-toml/update-all-references.sh index 7028b251ea0..4391499a1e1 100755 --- a/tests/ui-toml/update-all-references.sh +++ b/tests/ui-toml/update-all-references.sh @@ -1,18 +1,3 @@ #!/bin/bash -# -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -BUILD_DIR=$PWD/target/debug/test_build_base -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/tests/ui-toml/update-references.sh b/tests/ui-toml/update-references.sh deleted file mode 100755 index 2ab51168bca..00000000000 --- a/tests/ui-toml/update-references.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a foo.stderr file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi -done diff --git a/tests/ui/update-all-references.sh b/tests/ui/update-all-references.sh index 30ba9188db4..4391499a1e1 100755 --- a/tests/ui/update-all-references.sh +++ b/tests/ui/update-all-references.sh @@ -1,21 +1,3 @@ #!/bin/bash -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. - -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target} -PROFILE=${PROFILE:-debug} -BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base - -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/tests/ui/update-references.sh b/tests/ui/update-references.sh deleted file mode 100755 index e16ed600ef8..00000000000 --- a/tests/ui/update-references.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a `foo.stderr` file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - FIXED_NAME="${1/%.rs/.fixed}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then - echo updating "$MYDIR"/"$FIXED_NAME" - cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" - if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then - echo removing "$MYDIR"/"$FIXED_NAME" - rm "$MYDIR"/"$FIXED_NAME" - fi - fi -done From 41c562d4a578637306dca2d9a7889d8f5bb4a58e Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Thu, 10 Dec 2020 11:34:22 +0100 Subject: [PATCH 1084/1110] Improve variable naming --- clippy_dev/src/bless.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index 45e403fa74d..6a1fa61b12d 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -1,4 +1,4 @@ -//! `bless` updates the 'expected output' files in the repo with changed output files +//! `bless` updates the reference files in the repo with changed output files //! from the last test run. use std::env; @@ -28,35 +28,35 @@ pub fn bless() { .filter_map(Result::ok) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) .for_each(|f| { - update_test_file(f.path().with_extension("stdout")); - update_test_file(f.path().with_extension("stderr")); - update_test_file(f.path().with_extension("fixed")); + update_reference_file(f.path().with_extension("stdout")); + update_reference_file(f.path().with_extension("stderr")); + update_reference_file(f.path().with_extension("fixed")); }); } } -fn update_test_file(test_file_path: PathBuf) { - let build_output_path = build_dir().join(PathBuf::from(test_file_path.file_name().unwrap())); - let relative_test_file_path = test_file_path.strip_prefix(clippy_project_root()).unwrap(); +fn update_reference_file(reference_file_path: PathBuf) { + let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap())); + let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap(); // If compiletest did not write any changes during the test run, // we don't have to update anything - if !build_output_path.exists() { + if !test_output_path.exists() { return; } - let build_output = fs::read(&build_output_path).expect("Unable to read build output file"); - let test_file = fs::read(&test_file_path).expect("Unable to read test file"); + let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); + let reference_file = fs::read(&reference_file_path).expect("Unable to read reference file"); - if build_output != test_file { - // If a test run caused an output file to change, update the test file - println!("updating {}", &relative_test_file_path.display()); - fs::copy(build_output_path, &test_file_path).expect("Could not update test file"); + if test_output_file != reference_file { + // If a test run caused an output file to change, update the reference file + println!("updating {}", &relative_reference_file_path.display()); + fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file"); - if test_file.is_empty() { - // If we copied over an empty output file, we remove it - println!("removing {}", &relative_test_file_path.display()); - fs::remove_file(test_file_path).expect("Could not remove test file"); + if reference_file.is_empty() { + // If we copied over an empty output file, we remove the now empty reference file + println!("removing {}", &relative_reference_file_path.display()); + fs::remove_file(reference_file_path).expect("Could not remove reference file"); } } } From b8501e1be12594145bcd2bae2b47af2152785622 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 12 Dec 2020 15:14:54 +0100 Subject: [PATCH 1085/1110] Feed the dog :dog2: --- clippy_dev/src/bless.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index 6a1fa61b12d..8d5c2e95055 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -62,7 +62,7 @@ fn update_reference_file(reference_file_path: PathBuf) { } fn build_dir() -> PathBuf { - let profile = format!("{}", env::var("PROFILE").unwrap_or("debug".to_string())); + let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); let mut path = PathBuf::new(); path.push(CARGO_TARGET_DIR.clone()); path.push(profile); From 3af09b8cf1229fb05a549a13b144aca6b60784c7 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:32:41 +0200 Subject: [PATCH 1086/1110] New internal lint: interning_defined_symbol --- clippy_lints/src/lib.rs | 4 + clippy_lints/src/utils/internal_lints.rs | 80 +++++++++++++++++++ clippy_lints/src/utils/paths.rs | 6 ++ .../interning_defined_symbol.fixed | 33 ++++++++ tests/ui-internal/interning_defined_symbol.rs | 33 ++++++++ .../interning_defined_symbol.stderr | 27 +++++++ 6 files changed, 183 insertions(+) create mode 100644 tests/ui-internal/interning_defined_symbol.fixed create mode 100644 tests/ui-internal/interning_defined_symbol.rs create mode 100644 tests/ui-internal/interning_defined_symbol.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f06926fa91d..97018599b05 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -513,6 +513,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] + &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, @@ -958,6 +960,7 @@ 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 utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); @@ -1350,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), LintId::of(&utils::internal_lints::INVALID_PATHS), + LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 8b59a9541a7..0de87fab528 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; @@ -247,6 +248,30 @@ declare_clippy_lint! { "invalid path" } +declare_clippy_lint! { + /// **What it does:** + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// **Why is this bad?** + /// It's faster and easier to use the symbol constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -840,3 +865,58 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { } } } + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol to the constant name. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + // SAFETY: We're converting the raw bytes of the symbol value back + // into a Symbol instance. + let symbol = unsafe { std::mem::transmute::(value) }; + self.symbol_map.insert(symbol.to_string(), item.ident.to_string()); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + if let Some(symbol_const) = self.symbol_map.get(&arg); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + format!("rustc_span::symbol::sym::{}", symbol_const), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 6fdc7b4587f..2080a49a11c 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; +#[cfg(feature = "internal-lints")] +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; +#[cfg(feature = "internal-lints")] +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed new file mode 100644 index 00000000000..c6b84d2ef65 --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = rustc_span::symbol::sym::f32; + + // Using a sym macro + let _ = rustc_span::symbol::sym::f32; + + // Correct suggestion when symbol isn't stringified constant name + let _ = rustc_span::symbol::sym::proc_dash_macro; + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs new file mode 100644 index 00000000000..9ec82d4ad0b --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = Symbol::intern("f32"); + + // Using a sym macro + let _ = sym!(f32); + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr new file mode 100644 index 00000000000..74b906c8a57 --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -0,0 +1,27 @@ +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:17:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | +note: the lint level is defined here + --> $DIR/interning_defined_symbol.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:20:13 + | +LL | let _ = sym!(f32); + | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:23:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + +error: aborting due to 3 previous errors + From a6aa0acbeaeb74a50e08bfa2b18df4e22dbd9894 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:32:41 +0200 Subject: [PATCH 1087/1110] Fix dogfood errors --- clippy_lints/src/manual_ok_or.rs | 3 ++- clippy_lints/src/methods/mod.rs | 10 +++++----- clippy_lints/src/ref_option_ref.rs | 3 ++- clippy_lints/src/strings.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 5 +++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index c99d2e35b94..b97d97ea1a5 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -8,6 +8,7 @@ use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** @@ -51,7 +52,7 @@ impl LateLintPass<'_> for ManualOkOr { if args.len() == 3; let method_receiver = &args[0]; let ty = cx.typeck_results().expr_ty(method_receiver); - if is_type_diagnostic_item(cx, ty, sym!(option_type)); + if is_type_diagnostic_item(cx, ty, sym::option_type); let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..c5eab2a97fe 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1568,7 +1568,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); - if args.len() == 1 && method_call.ident.name == sym!(clone) { + if args.len() == 1 && method_call.ident.name == sym::clone { lint_clone_on_copy(cx, expr, &args[0], self_ty); lint_clone_on_ref_ptr(cx, expr, &args[0]); } @@ -1592,7 +1592,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } }, - ty::Ref(..) if method_call.ident.name == sym!(into_iter) => { + ty::Ref(..) if method_call.ident.name == sym::into_iter => { lint_into_iter(cx, expr, self_ty, *method_span); }, _ => (), @@ -2638,9 +2638,9 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((EXPECT_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { Some((EXPECT_USED, "a Result", "Err")) } else { None @@ -3133,7 +3133,7 @@ fn lint_search_is_some<'tcx>( else if search_method == "find" { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, self_ty, sym::string_type) { true } else { *self_ty.kind() == ty::Str diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index a914a77d48b..803ebada54b 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -2,6 +2,7 @@ use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use if_chain::if_chain; use rustc_errors::Applicability; @@ -41,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(res) = last.res; if let Some(def_id) = res.opt_def_id(); - if cx.tcx.is_diagnostic_item(sym!(option_type), def_id); + if cx.tcx.is_diagnostic_item(sym::option_type, def_id); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 77e79073378..31dd5965473 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -372,7 +372,7 @@ impl LateLintPass<'_> for StringToString { if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; if path.ident.name == sym!(to_string); let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(string_type)); + if is_type_diagnostic_item(cx, ty, sym::string_type); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index e763da593d4..5b9a80f92db 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -9,6 +9,7 @@ use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use rustc_span::Span; declare_clippy_lint! { @@ -82,9 +83,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } } - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) { ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { ("Result", &paths::RESULT_OK) } else { return; From 64e630c28018972479394a2fbdcc9f7d8856bb91 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:46:08 +0200 Subject: [PATCH 1088/1110] Run 'cargo dev update_lints' --- clippy_lints/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97018599b05..0498d469c00 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -511,10 +511,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] &utils::internal_lints::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal-lints")] + &utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, @@ -1352,8 +1352,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), - LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), + LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), From cd2a62cb0cf89f5b4105c1c40651cf0eeaa85b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 13 Dec 2020 15:17:47 +0100 Subject: [PATCH 1089/1110] needless_borrow: print the type in the lint message changelog: needless_borrow: print type in lint message --- clippy_lints/src/needless_borrow.rs | 7 +++++-- tests/ui/eta.stderr | 2 +- tests/ui/needless_borrow.stderr | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 405c21d608d..bff53eb8cca 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { kind: Adjust::Deref(_), .. @@ -62,8 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { cx, NEEDLESS_BORROW, e.span, - "this expression borrows a reference that is immediately dereferenced \ + &format!( + "this expression borrows a reference (`&{}`) that is immediately dereferenced \ by the compiler", + ty + ), |diag| { if let Some(snippet) = snippet_opt(cx, inner.span) { diag.span_suggestion( diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index c4713ca8083..16aa1b07733 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -12,7 +12,7 @@ error: redundant closure found LL | meta(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler --> $DIR/eta.rs:24:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 0bfeda7914d..bea4b41b803 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,4 +1,4 @@ -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:14:15 | LL | let c = x(&&a); @@ -12,7 +12,7 @@ error: this pattern creates a reference to a reference LL | if let Some(ref cake) = Some(&5) {} | ^^^^^^^^ help: change this to: `cake` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:28:15 | LL | 46 => &&a, From cc9695543ea8f3973a2be2936df0efc724de1c16 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 11 Dec 2020 23:54:47 +0100 Subject: [PATCH 1090/1110] Pass Clippy args also trough RUSTFLAGS --- README.md | 1 - src/driver.rs | 116 +++++++++++++++++++++++++++++++++++------------ src/main.rs | 98 ++++++++++++++++++++++++++++++--------- tests/dogfood.rs | 2 +- 4 files changed, 165 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index aaa55e11c7d..dc931963726 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,6 @@ the lint(s) you are interested in: ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` -Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ### Specifying the minimum supported Rust version diff --git a/src/driver.rs b/src/driver.rs index e490ee54c0b..40f1b802e60 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,5 +1,6 @@ #![feature(rustc_private)] #![feature(once_cell)] +#![feature(bool_to_option)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -19,6 +20,7 @@ use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; +use std::iter; use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; @@ -47,20 +49,6 @@ fn arg_value<'a, T: Deref>( None } -#[test] -fn test_arg_value() { - let args = &["--bar=bar", "--foobar", "123", "--foo"]; - - assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); - assert_eq!(arg_value(args, "--bar", |_| false), None); - assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); - assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); - assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); - assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); - assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); - assert_eq!(arg_value(args, "--foo", |_| true), None); -} - struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} @@ -182,6 +170,28 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option(args: &mut Vec, clippy_args: I) +where + T: AsRef, + U: AsRef + ?Sized + 'a, + I: Iterator + Clone, +{ + let args_iter = clippy_args.map(AsRef::as_ref); + let args_count = args_iter.clone().count(); + + if args_count > 0 { + if let Some(start) = args.windows(args_count).enumerate().find_map(|(current, window)| { + window + .iter() + .map(AsRef::as_ref) + .eq(args_iter.clone()) + .then_some(current) + }) { + args.drain(start..start + args_count); + } + } +} + #[allow(clippy::too_many_lines)] pub fn main() { rustc_driver::init_rustc_env_logger(); @@ -278,20 +288,9 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; - let mut no_deps = false; - let clippy_args = env::var("CLIPPY_ARGS") - .unwrap_or_default() - .split("__CLIPPY_HACKERY__") - .filter_map(|s| match s { - "" => None, - "--no-deps" => { - no_deps = true; - None - }, - _ => Some(s.to_string()), - }) - .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) - .collect::>(); + let clippy_args = env::var("CLIPPY_ARGS").unwrap_or_default(); + let clippy_args = clippy_args.split_whitespace(); + let no_deps = clippy_args.clone().any(|flag| flag == "--no-deps"); // We enable Clippy if one of the following conditions is met // - IF Clippy is run on its test suite OR @@ -304,7 +303,11 @@ pub fn main() { let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); if clippy_enabled { - args.extend(clippy_args); + remove_clippy_args(&mut args, iter::once("--no-deps")); + args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); + } else { + // Remove all flags passed through RUSTFLAGS if Clippy is not enabled. + remove_clippy_args(&mut args, clippy_args); } let mut clippy = ClippyCallbacks; @@ -315,3 +318,58 @@ pub fn main() { rustc_driver::RunCompiler::new(&args, callbacks).run() })) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arg_value() { + let args = &["--bar=bar", "--foobar", "123", "--foo"]; + + assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); + assert_eq!(arg_value(args, "--bar", |_| false), None); + assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); + assert_eq!(arg_value(args, "--foo", |_| true), None); + } + + #[test] + fn removes_clippy_args_from_start() { + let mut args = vec!["-D", "clippy::await_holding_lock", "--cfg", r#"feature="some_feat""#]; + let clippy_args = ["-D", "clippy::await_holding_lock"].iter(); + + remove_clippy_args(&mut args, clippy_args); + assert_eq!(args, &["--cfg", r#"feature="some_feat""#]); + } + + #[test] + fn removes_clippy_args_from_end() { + let mut args = vec!["-Zui-testing", "-A", "clippy::empty_loop", "--no-deps"]; + let clippy_args = ["-A", "clippy::empty_loop", "--no-deps"].iter(); + + remove_clippy_args(&mut args, clippy_args); + assert_eq!(args, &["-Zui-testing"]); + } + + #[test] + fn removes_clippy_args_from_middle() { + let mut args = vec!["-Zui-testing", "-W", "clippy::filter_map", "-L", "serde"]; + let clippy_args = ["-W", "clippy::filter_map"].iter(); + + remove_clippy_args(&mut args, clippy_args); + assert_eq!(args, &["-Zui-testing", "-L", "serde"]); + } + + #[test] + fn no_clippy_args_to_remove() { + let mut args = vec!["-Zui-testing", "-L", "serde"]; + let clippy_args: [&str; 0] = []; + + remove_clippy_args(&mut args, clippy_args.iter()); + assert_eq!(args, &["-Zui-testing", "-L", "serde"]); + } +} diff --git a/src/main.rs b/src/main.rs index ea06743394d..7594ea2c7b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(bool_to_option)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -62,11 +63,12 @@ struct ClippyCmd { unstable_options: bool, cargo_subcommand: &'static str, args: Vec, - clippy_args: Vec, + rustflags: Option, + clippy_args: Option, } impl ClippyCmd { - fn new(mut old_args: I) -> Self + fn new(mut old_args: I, rustflags: Option) -> Self where I: Iterator, { @@ -99,16 +101,19 @@ impl ClippyCmd { args.insert(0, "+nightly".to_string()); } - let mut clippy_args: Vec = old_args.collect(); - if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { - clippy_args.push("--no-deps".into()); + let mut clippy_args = old_args.collect::>().join(" "); + if cargo_subcommand == "fix" && !clippy_args.contains("--no-deps") { + clippy_args = format!("{} --no-deps", clippy_args); } + let has_args = !clippy_args.is_empty(); ClippyCmd { unstable_options, cargo_subcommand, args, - clippy_args, + rustflags: has_args + .then(|| rustflags.map_or_else(|| clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags))), + clippy_args: has_args.then_some(clippy_args), } } @@ -150,18 +155,19 @@ impl ClippyCmd { fn into_std_cmd(self) -> Command { let mut cmd = Command::new("cargo"); - let clippy_args: String = self - .clippy_args - .iter() - .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) - .collect(); cmd.env(self.path_env(), Self::path()) .envs(ClippyCmd::target_dir()) - .env("CLIPPY_ARGS", clippy_args) .arg(self.cargo_subcommand) .args(&self.args); + // HACK: pass Clippy args to the driver *also* through RUSTFLAGS. + // This guarantees that new builds will be triggered when Clippy flags change. + if let (Some(clippy_args), Some(rustflags)) = (self.clippy_args, self.rustflags) { + cmd.env("CLIPPY_ARGS", clippy_args); + cmd.env("RUSTFLAGS", rustflags); + } + cmd } } @@ -170,7 +176,7 @@ fn process(old_args: I) -> Result<(), i32> where I: Iterator, { - let cmd = ClippyCmd::new(old_args); + let cmd = ClippyCmd::new(old_args, env::var("RUSTFLAGS").ok()); let mut cmd = cmd.into_std_cmd(); @@ -195,7 +201,7 @@ mod tests { #[should_panic] fn fix_without_unstable() { let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string); - let _ = ClippyCmd::new(args); + let _ = ClippyCmd::new(args, None); } #[test] @@ -203,7 +209,8 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); + let cmd = ClippyCmd::new(args, None); + assert_eq!("fix", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options"))); @@ -214,8 +221,9 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); - assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps")); + let cmd = ClippyCmd::new(args, None); + + assert!(cmd.clippy_args.unwrap().contains("--no-deps")); } #[test] @@ -223,14 +231,16 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options -- --no-deps" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); - assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1); + let cmd = ClippyCmd::new(args, None); + + assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count()); } #[test] fn check() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let cmd = ClippyCmd::new(args); + let cmd = ClippyCmd::new(args, None); + assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WRAPPER", cmd.path_env()); } @@ -240,8 +250,54 @@ mod tests { let args = "cargo clippy -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); + let cmd = ClippyCmd::new(args, None); + assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); } + + #[test] + fn clippy_args_into_rustflags() { + let args = "cargo clippy -- -W clippy::as_conversions" + .split_whitespace() + .map(ToString::to_string); + let rustflags = None; + let cmd = ClippyCmd::new(args, rustflags); + + assert_eq!("-W clippy::as_conversions", cmd.rustflags.unwrap()); + } + + #[test] + fn clippy_args_respect_existing_rustflags() { + let args = "cargo clippy -- -D clippy::await_holding_lock" + .split_whitespace() + .map(ToString::to_string); + let rustflags = Some(r#"--cfg feature="some_feat""#.into()); + let cmd = ClippyCmd::new(args, rustflags); + + assert_eq!( + r#"-D clippy::await_holding_lock --cfg feature="some_feat""#, + cmd.rustflags.unwrap() + ); + } + + #[test] + fn no_env_change_if_no_clippy_args() { + let args = "cargo clippy".split_whitespace().map(ToString::to_string); + let rustflags = Some(r#"--cfg feature="some_feat""#.into()); + let cmd = ClippyCmd::new(args, rustflags); + + assert!(cmd.clippy_args.is_none()); + assert!(cmd.rustflags.is_none()); + } + + #[test] + fn no_env_change_if_no_clippy_args_nor_rustflags() { + let args = "cargo clippy".split_whitespace().map(ToString::to_string); + let rustflags = None; + let cmd = ClippyCmd::new(args, rustflags); + + assert!(cmd.clippy_args.is_none()); + assert!(cmd.rustflags.is_none()); + } } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 052223d6d6f..fda1413868e 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -23,7 +23,7 @@ fn dogfood_clippy() { .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") + .arg("clippy") .arg("--all-targets") .arg("--all-features") .arg("--") From f93d9654d2ce012e146b8dfa615ad724f4bb23fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 13 Dec 2020 17:21:53 +0100 Subject: [PATCH 1091/1110] Address comments from PR review Also: enable tests for cargo-clippy --- Cargo.toml | 1 - src/main.rs | 76 +++++++++++++++++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a765390c603..7f9d22e594b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ publish = false [[bin]] name = "cargo-clippy" -test = false path = "src/main.rs" [[bin]] diff --git a/src/main.rs b/src/main.rs index 7594ea2c7b1..1c0e04689a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(bool_to_option)] +#![feature(command_access)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -63,12 +64,11 @@ struct ClippyCmd { unstable_options: bool, cargo_subcommand: &'static str, args: Vec, - rustflags: Option, clippy_args: Option, } impl ClippyCmd { - fn new(mut old_args: I, rustflags: Option) -> Self + fn new(mut old_args: I) -> Self where I: Iterator, { @@ -111,8 +111,6 @@ impl ClippyCmd { unstable_options, cargo_subcommand, args, - rustflags: has_args - .then(|| rustflags.map_or_else(|| clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags))), clippy_args: has_args.then_some(clippy_args), } } @@ -153,7 +151,7 @@ impl ClippyCmd { .map(|p| ("CARGO_TARGET_DIR", p)) } - fn into_std_cmd(self) -> Command { + fn into_std_cmd(self, rustflags: Option) -> Command { let mut cmd = Command::new("cargo"); cmd.env(self.path_env(), Self::path()) @@ -163,9 +161,12 @@ impl ClippyCmd { // HACK: pass Clippy args to the driver *also* through RUSTFLAGS. // This guarantees that new builds will be triggered when Clippy flags change. - if let (Some(clippy_args), Some(rustflags)) = (self.clippy_args, self.rustflags) { + if let Some(clippy_args) = self.clippy_args { + cmd.env( + "RUSTFLAGS", + rustflags.map_or(clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags)), + ); cmd.env("CLIPPY_ARGS", clippy_args); - cmd.env("RUSTFLAGS", rustflags); } cmd @@ -176,9 +177,9 @@ fn process(old_args: I) -> Result<(), i32> where I: Iterator, { - let cmd = ClippyCmd::new(old_args, env::var("RUSTFLAGS").ok()); + let cmd = ClippyCmd::new(old_args); - let mut cmd = cmd.into_std_cmd(); + let mut cmd = cmd.into_std_cmd(env::var("RUSTFLAGS").ok()); let exit_status = cmd .spawn() @@ -196,12 +197,13 @@ where #[cfg(test)] mod tests { use super::ClippyCmd; + use std::ffi::OsStr; #[test] #[should_panic] fn fix_without_unstable() { let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string); - let _ = ClippyCmd::new(args, None); + let _ = ClippyCmd::new(args); } #[test] @@ -209,7 +211,7 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args, None); + let cmd = ClippyCmd::new(args); assert_eq!("fix", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); @@ -221,7 +223,7 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args, None); + let cmd = ClippyCmd::new(args); assert!(cmd.clippy_args.unwrap().contains("--no-deps")); } @@ -231,7 +233,7 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options -- --no-deps" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args, None); + let cmd = ClippyCmd::new(args); assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count()); } @@ -239,7 +241,7 @@ mod tests { #[test] fn check() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let cmd = ClippyCmd::new(args, None); + let cmd = ClippyCmd::new(args); assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WRAPPER", cmd.path_env()); @@ -250,7 +252,7 @@ mod tests { let args = "cargo clippy -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args, None); + let cmd = ClippyCmd::new(args); assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); @@ -261,10 +263,14 @@ mod tests { let args = "cargo clippy -- -W clippy::as_conversions" .split_whitespace() .map(ToString::to_string); - let rustflags = None; - let cmd = ClippyCmd::new(args, rustflags); + let cmd = ClippyCmd::new(args); - assert_eq!("-W clippy::as_conversions", cmd.rustflags.unwrap()); + let rustflags = None; + let cmd = cmd.into_std_cmd(rustflags); + + assert!(cmd + .get_envs() + .any(|(key, val)| key == "RUSTFLAGS" && val == Some(OsStr::new("-W clippy::as_conversions")))); } #[test] @@ -272,32 +278,38 @@ mod tests { let args = "cargo clippy -- -D clippy::await_holding_lock" .split_whitespace() .map(ToString::to_string); - let rustflags = Some(r#"--cfg feature="some_feat""#.into()); - let cmd = ClippyCmd::new(args, rustflags); + let cmd = ClippyCmd::new(args); - assert_eq!( - r#"-D clippy::await_holding_lock --cfg feature="some_feat""#, - cmd.rustflags.unwrap() - ); + let rustflags = Some(r#"--cfg feature="some_feat""#.into()); + let cmd = cmd.into_std_cmd(rustflags); + + assert!(cmd.get_envs().any(|(key, val)| key == "RUSTFLAGS" + && val == Some(OsStr::new(r#"-D clippy::await_holding_lock --cfg feature="some_feat""#)))); } #[test] fn no_env_change_if_no_clippy_args() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let rustflags = Some(r#"--cfg feature="some_feat""#.into()); - let cmd = ClippyCmd::new(args, rustflags); + let cmd = ClippyCmd::new(args); - assert!(cmd.clippy_args.is_none()); - assert!(cmd.rustflags.is_none()); + let rustflags = Some(r#"--cfg feature="some_feat""#.into()); + let cmd = cmd.into_std_cmd(rustflags); + + assert!(!cmd + .get_envs() + .any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS")); } #[test] fn no_env_change_if_no_clippy_args_nor_rustflags() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let rustflags = None; - let cmd = ClippyCmd::new(args, rustflags); + let cmd = ClippyCmd::new(args); - assert!(cmd.clippy_args.is_none()); - assert!(cmd.rustflags.is_none()); + let rustflags = None; + let cmd = cmd.into_std_cmd(rustflags); + + assert!(!cmd + .get_envs() + .any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS")) } } From 404c50f56200c28ebbee64113c9dfc0120c33e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 13 Dec 2020 15:22:45 +0100 Subject: [PATCH 1092/1110] NFC: clippy cargo dev: move generation of clap config into a function --- clippy_dev/src/main.rs | 100 +++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index f66855620e7..5938b788101 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,10 +1,52 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] -use clap::{App, Arg, SubCommand}; +use clap::{App, Arg, ArgMatches, SubCommand}; use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; fn main() { - let matches = App::new("Clippy developer tooling") + let matches = get_clap_config(); + + match matches.subcommand() { + ("bless", Some(_)) => { + bless::bless(); + }, + ("fmt", Some(matches)) => { + fmt::run(matches.is_present("check"), matches.is_present("verbose")); + }, + ("update_lints", Some(matches)) => { + if matches.is_present("print-only") { + update_lints::print_lints(); + } else if matches.is_present("check") { + update_lints::run(update_lints::UpdateMode::Check); + } else { + update_lints::run(update_lints::UpdateMode::Change); + } + }, + ("new_lint", Some(matches)) => { + match new_lint::create( + matches.value_of("pass"), + matches.value_of("name"), + matches.value_of("category"), + ) { + Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Err(e) => eprintln!("Unable to create lint: {}", e), + } + }, + ("limit_stderr_length", _) => { + stderr_length_check::check(); + }, + ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + ("serve", Some(matches)) => { + let port = matches.value_of("port").unwrap().parse().unwrap(); + let lint = matches.value_of("lint"); + serve::run(port, lint); + }, + _ => {}, + } +} + +fn get_clap_config<'a>() -> ArgMatches<'a> { + App::new("Clippy developer tooling") .subcommand(SubCommand::with_name("bless").about("bless the test output changes")) .subcommand( SubCommand::with_name("fmt") @@ -26,16 +68,16 @@ fn main() { .about("Updates lint registration and information from the source code") .long_about( "Makes sure that:\n \ - * the lint count in README.md is correct\n \ - * the changelog contains markdown link references at the bottom\n \ - * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ - * all lints are registered in the lint store", + * the lint count in README.md is correct\n \ + * the changelog contains markdown link references at the bottom\n \ + * all lint groups include the correct lints\n \ + * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \ + * all lints are registered in the lint store", ) .arg(Arg::with_name("print-only").long("print-only").help( "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", + This does not include deprecated and internal lints. \ + (Does not modify any files)", )) .arg( Arg::with_name("check") @@ -114,43 +156,5 @@ fn main() { ) .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), ) - .get_matches(); - - match matches.subcommand() { - ("bless", Some(_)) => { - bless::bless(); - }, - ("fmt", Some(matches)) => { - fmt::run(matches.is_present("check"), matches.is_present("verbose")); - }, - ("update_lints", Some(matches)) => { - if matches.is_present("print-only") { - update_lints::print_lints(); - } else if matches.is_present("check") { - update_lints::run(update_lints::UpdateMode::Check); - } else { - update_lints::run(update_lints::UpdateMode::Change); - } - }, - ("new_lint", Some(matches)) => { - match new_lint::create( - matches.value_of("pass"), - matches.value_of("name"), - matches.value_of("category"), - ) { - Ok(_) => update_lints::run(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {}", e), - } - }, - ("limit_stderr_length", _) => { - stderr_length_check::check(); - }, - ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), - ("serve", Some(matches)) => { - let port = matches.value_of("port").unwrap().parse().unwrap(); - let lint = matches.value_of("lint"); - serve::run(port, lint); - }, - _ => {}, - } + .get_matches() } From 91fa25c9de9b8abb410273fe233fd71c39a434e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 13 Dec 2020 15:49:48 +0100 Subject: [PATCH 1093/1110] clippy dev fmt: don't format if we have a local rustc repo enabled as path dependency via cargo dev ra-setup. rustfmt would try to format the entire rustc repo, probably because it sees it as a local dependency. --- clippy_dev/src/fmt.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 6ae3f58c1f2..6f13af8aa47 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,9 +1,9 @@ use crate::clippy_project_root; use shell_escape::escape; use std::ffi::OsStr; -use std::io; use std::path::Path; use std::process::{self, Command}; +use std::{fs, io}; use walkdir::WalkDir; #[derive(Debug)] @@ -12,6 +12,7 @@ pub enum CliError { IoError(io::Error), RustfmtNotInstalled, WalkDirError(walkdir::Error), + RaSetupActive, } impl From for CliError { @@ -31,12 +32,23 @@ struct FmtContext { verbose: bool, } +// the "main" function of cargo dev fmt pub fn run(check: bool, verbose: bool) { fn try_run(context: &FmtContext) -> Result { let mut success = true; let project_root = clippy_project_root(); + // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to + // format because rustfmt would also format the entire rustc repo as it is a local + // dependency + if fs::read_to_string(project_root.join("Cargo.toml")) + .expect("Failed to read clippy Cargo.toml") + .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + { + return Err(CliError::RaSetupActive); + } + rustfmt_test(context)?; success &= cargo_fmt(context, project_root.as_path())?; @@ -75,6 +87,13 @@ pub fn run(check: bool, verbose: bool) { CliError::WalkDirError(err) => { eprintln!("error: {}", err); }, + CliError::RaSetupActive => { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev ra-setup`. +Not formatting because that would format the local repo as well! +Please revert the changes to Cargo.tomls first." + ); + }, } } From 27dc565d28444fa488972e09a8117474cee1e752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 13 Dec 2020 17:01:44 +0100 Subject: [PATCH 1094/1110] cargo dev: rename ra-setup to ra_setup to be in line with the other commands --- CONTRIBUTING.md | 8 ++++---- clippy_dev/src/fmt.rs | 2 +- clippy_dev/src/main.rs | 4 ++-- clippy_dev/src/ra_setup.rs | 2 +- doc/basics.md | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a3c602b9e2..49b64da1fb6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,10 +19,10 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) - [How Clippy works](#how-clippy-works) - - [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust) + - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust) - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) - - [Performing the sync](#performing-the-sync) - - [Syncing back changes in Clippy to [`rust-lang/rust`]](#syncing-back-changes-in-clippy-to-rust-langrust) + - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) + - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust) - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) @@ -111,7 +111,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava `git clone https://github.com/rust-lang/rust/`. Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies which rust-analyzer will be able to understand. -Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo +Run `cargo dev ra_setup --repo-path ` where `` is an absolute path to the rustc repo you just cloned. The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 6f13af8aa47..6b528d219df 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -89,7 +89,7 @@ pub fn run(check: bool, verbose: bool) { }, CliError::RaSetupActive => { eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev ra-setup`. + "error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`. Not formatting because that would format the local repo as well! Please revert the changes to Cargo.tomls first." ); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 5938b788101..4fdae38e3ab 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -35,7 +35,7 @@ fn main() { ("limit_stderr_length", _) => { stderr_length_check::check(); }, - ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + ("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), ("serve", Some(matches)) => { let port = matches.value_of("port").unwrap().parse().unwrap(); let lint = matches.value_of("lint"); @@ -131,7 +131,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .about("Ensures that stderr files do not grow longer than a certain amount of lines."), ) .subcommand( - SubCommand::with_name("ra-setup") + SubCommand::with_name("ra_setup") .about("Alter dependencies so rust-analyzer can find rustc internals") .arg( Arg::with_name("rustc-repo-path") diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index 9d9e836cc08..40bf4a9505a 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -52,7 +52,7 @@ fn inject_deps_into_manifest( // do not inject deps if we have aleady done so if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { eprintln!( - "cargo dev ra-setup: warning: deps already found inside {}, doing nothing.", + "cargo dev ra_setup: warning: deps already found inside {}, doing nothing.", manifest_path ); return Ok(()); diff --git a/doc/basics.md b/doc/basics.md index dc71f022773..954474a17aa 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -8,7 +8,7 @@ the codebase take a look at [Adding Lints] or [Common Tools]. [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md - [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) - - [Get the code](#get-the-code) + - [Get the Code](#get-the-code) - [Building and Testing](#building-and-testing) - [`cargo dev`](#cargo-dev) - [PR](#pr) @@ -87,7 +87,7 @@ cargo dev update_lints # create a new lint and register it cargo dev new_lint # (experimental) Setup Clippy to work with rust-analyzer -cargo dev ra-setup +cargo dev ra_setup ``` ## PR From a37af06fea574f3d9f4d3cca58a70cd545523486 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 9 Dec 2020 12:25:45 +0000 Subject: [PATCH 1095/1110] Removing false positive for the match_single_binding lint * Applying suggested changes from the PR --- clippy_lints/src/matches.rs | 22 +++++++++++++++++-- tests/ui/match_single_binding.fixed | 28 +++++++++++++++++++++++ tests/ui/match_single_binding.rs | 33 ++++++++++++++++++++++++++++ tests/ui/match_single_binding.stderr | 13 ++++++++++- 4 files changed, 93 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2a1a73f98ee..04b35835c6b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -4,8 +4,8 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, - snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -1237,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } + + // HACK: + // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here + // to prevent false positives as there is currently no better way to detect if code was excluded by + // a macro. See PR #6435 + if_chain! { + if let Some(match_snippet) = snippet_opt(cx, expr.span); + if let Some(arm_snippet) = snippet_opt(cx, arms[0].span); + if let Some(ex_snippet) = snippet_opt(cx, ex.span); + let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, ""); + if rest_snippet.contains("=>"); + then { + // The code it self contains another thick arrow "=>" + // -> Either another arm or a comment + return; + } + } + let matched_vars = ex.span; let bind_names = arms[0].pat.span; let match_body = remove_blocks(&arms[0].body); diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index f3627902eec..526e94b10bd 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -87,4 +87,32 @@ fn main() { unwrapped }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + println!("Single branch"); + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 8c182148ae1..6a2ca7c5e93 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -99,4 +99,37 @@ fn main() { unwrapped => unwrapped, }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 795c8c3e24d..cbbf5d29c02 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -167,5 +167,16 @@ LL | unwrapped LL | }) | -error: aborting due to 11 previous errors +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:112:5 + | +LL | / match match y { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("Single branch");` + +error: aborting due to 12 previous errors From 426aba2ef4a00c5041b65116f542d3840a519e96 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 14 Dec 2020 11:57:35 +0900 Subject: [PATCH 1096/1110] Fix links in CONTRIBUTING.md --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49b64da1fb6..d2a3c52dab9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,10 +19,10 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) - [How Clippy works](#how-clippy-works) - - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust) + - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust) - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) - - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) - - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust) + - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) + - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust) - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) From 8b1e9ed85b180553ce74af1555ad7d980f79667c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Mon, 14 Dec 2020 17:47:05 +0100 Subject: [PATCH 1097/1110] bump pinned nightly from nightly-2020-12-09 to nightly-2020-12-14 This should hopefully fix incremental compilation ICEs from rustc that I have been encountering multiple times while working with the previous nightly. --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index dfa7dea449a..ac51154f8a9 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2020-12-09" +channel = "nightly-2020-12-14" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 39bcf8e55438016fb86427f9b8a78b6a32a84126 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 15 Dec 2020 23:18:03 +0100 Subject: [PATCH 1098/1110] Handle fatal errors when parsing doctests --- clippy_lints/src/doc.rs | 112 ++++++++++++++++++---------------- clippy_lints/src/lib.rs | 1 + tests/ui/needless_doc_main.rs | 6 ++ 3 files changed, 66 insertions(+), 53 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 55e4755c278..77203a286dd 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -451,66 +451,72 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - fn has_needless_main(code: &str) -> bool { - let filename = FileName::anon_source_code(code); + fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool { + rustc_driver::catch_fatal_errors(|| { + rustc_span::with_session_globals(cx.tcx.sess.edition(), || { + let filename = FileName::anon_source_code(code); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); - let handler = Handler::with_emitter(false, None, box emitter); - let sess = ParseSess::with_span_handler(handler, sm); + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { - Ok(p) => p, - Err(errs) => { - for mut err in errs { - err.cancel(); - } - return false; - }, - }; - - let mut relevant_main_found = false; - loop { - match parser.parse_item() { - 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(_, sig, _, Some(block)) if item.ident.name == sym::main => { - let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); - let returns_nothing = match &sig.decl.output { - FnRetTy::Default(..) => true, - FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - _ => false, - }; - - if returns_nothing && !is_async && !block.stmts.is_empty() { - // This main function should be linted, but only if there are no other functions - relevant_main_found = true; - } else { - // This main function should not be linted, we're done - return false; + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); } + return false; }, - // Another function was found; this case is ignored too - ItemKind::Fn(..) => return false, - _ => {}, - }, - Ok(None) => break, - Err(mut e) => { - e.cancel(); - return false; - }, - } - } + }; - relevant_main_found + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + 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(_, sig, _, Some(block)) if item.ident.name == sym::main => { + let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + }) + }) + .ok() + .unwrap_or_default() } - if has_needless_main(text) { + if has_needless_main(cx, text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f06926fa91d..651b9e71131 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -27,6 +27,7 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_pretty; diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 883683e08a2..52c84089fa8 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -128,6 +128,12 @@ fn bad_doctests() {} /// ``` fn no_false_positives() {} +/// Yields a parse error when interpreted as rust code: +/// ``` +/// r#"hi" +/// ``` +fn issue_6022() {} + fn main() { bad_doctests(); no_false_positives(); From 41b5ebebfdc583d9e3702dd61c8d897999d1e7f5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 16 Dec 2020 00:14:47 +0100 Subject: [PATCH 1099/1110] needless_doctest_main: add edition support --- clippy_lints/src/doc.rs | 26 ++++++++++++++++++-------- tests/ui/needless_doc_main.rs | 4 ++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 77203a286dd..08134cc16c0 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -14,6 +14,7 @@ use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::edition::Edition; use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; use std::io; @@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs check_doc(cx, valid_idents, events, &spans) } -const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"]; +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, @@ -400,13 +401,21 @@ fn check_doc<'a, Events: Iterator, Range { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { - is_rust = - lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i)); + let infos = lang.split(',').collect::>(); + is_rust = !infos.iter().any(|&i| i == "ignore") + && infos + .iter() + .any(|i| i.is_empty() || i.starts_with("edition") || RUST_CODE.contains(&i)); + edition = infos + .iter() + .find_map(|i| i.starts_with("edition").then(|| i[7..].parse::().ok())) + .flatten(); } }, End(CodeBlock(_)) => { @@ -436,7 +445,8 @@ fn check_doc<'a, Events: Iterator, Range, Range, text: &str, span: Span) { - fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool { +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { + fn has_needless_main(code: &str, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { - rustc_span::with_session_globals(cx.tcx.sess.edition(), || { + rustc_span::with_session_globals(edition, || { let filename = FileName::anon_source_code(code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); @@ -516,7 +526,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { .unwrap_or_default() } - if has_needless_main(cx, text) { + if has_needless_main(text, edition) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 52c84089fa8..83e9bbaa3af 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// ``` /// /// With an explicit return type it should lint too -/// ``` +/// ```edition2015 /// fn main() -> () { /// unimplemented!(); /// } @@ -39,7 +39,7 @@ fn bad_doctests() {} /// ``` /// /// This shouldn't lint either, because main is async: -/// ``` +/// ```edition2018 /// async fn main() { /// assert_eq!(42, ANSWER); /// } From f732cc5cd6aeb06e07bd478d78fccaa625daa685 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Wed, 16 Dec 2020 06:05:25 +0200 Subject: [PATCH 1100/1110] Remove unsafe code --- clippy_lints/src/utils/internal_lints.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0de87fab528..9ba39f73ee8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -868,8 +868,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { #[derive(Default)] pub struct InterningDefinedSymbol { - // Maps the symbol to the constant name. - symbol_map: FxHashMap, + // Maps the symbol value to the constant name. + symbol_map: FxHashMap, } impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); @@ -889,10 +889,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); if let Ok(value) = value.to_u32(); then { - // SAFETY: We're converting the raw bytes of the symbol value back - // into a Symbol instance. - let symbol = unsafe { std::mem::transmute::(value) }; - self.symbol_map.insert(symbol.to_string(), item.ident.to_string()); + self.symbol_map.insert(value, item.ident.to_string()); } } } @@ -905,7 +902,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - if let Some(symbol_const) = self.symbol_map.get(&arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(symbol_const) = self.symbol_map.get(&value); then { span_lint_and_sugg( cx, From 1d6fac616767d84156e018e2a78582b7164241fb Mon Sep 17 00:00:00 2001 From: Naughty Date: Wed, 16 Dec 2020 20:48:03 -0300 Subject: [PATCH 1101/1110] Typo: std::fs::crate_dir -> std::fs::create_dir --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 4002fb655a5..200b6a565cc 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. /// - /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. /// /// **Known problems:** None. /// From bb68ec6cfc76a6ec69d21c0f64dbc4aa99158958 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 17 Dec 2020 15:24:44 +0100 Subject: [PATCH 1102/1110] Apply suggestion from PR review --- clippy_lints/src/doc.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 08134cc16c0..aba65532795 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -407,15 +407,18 @@ fn check_doc<'a, Events: Iterator, Range { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { - let infos = lang.split(',').collect::>(); - is_rust = !infos.iter().any(|&i| i == "ignore") - && infos - .iter() - .any(|i| i.is_empty() || i.starts_with("edition") || RUST_CODE.contains(&i)); - edition = infos - .iter() - .find_map(|i| i.starts_with("edition").then(|| i[7..].parse::().ok())) - .flatten(); + for item in lang.split(',') { + if item == "ignore" { + is_rust = false; + break; + } + if let Some(stripped) = item.strip_prefix("edition") { + is_rust = true; + edition = stripped.parse::().ok(); + } else if item.is_empty() || RUST_CODE.contains(&item) { + is_rust = true; + } + } } }, End(CodeBlock(_)) => { From 1eb7608a2e7284eff2d0ba80705f1cf28a1117e5 Mon Sep 17 00:00:00 2001 From: Andrew Houts Date: Thu, 17 Dec 2020 21:09:55 -0600 Subject: [PATCH 1103/1110] make needless_update ignore non_exhaustive structs --- clippy_lints/src/needless_update.rs | 21 +++++++++++++++++++-- tests/ui/needless_update.rs | 11 +++++++++++ tests/ui/needless_update.stderr | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 98e9078094a..1e387b518c3 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -21,7 +21,14 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// + /// # + /// # #[non_exhaustive] + /// # struct Options { + /// # a: bool, + /// # b: i32, + /// # } + /// # let default_options = Options { a: false, b: 0 }; + /// # /// // Bad /// Point { /// x: 1, @@ -36,6 +43,14 @@ declare_clippy_lint! { /// y: 1, /// ..zero_point /// }; + /// + /// // this lint is not applied to structs marked with [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html) + /// // Ok + /// Options { + /// a: true, + /// b: 321, + /// ..default_options + /// }; /// ``` pub NEEDLESS_UPDATE, complexity, @@ -49,7 +64,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() { + if fields.len() == def.non_enum_variant().fields.len() + && !def.variants[0_usize.into()].is_field_list_non_exhaustive() + { span_lint( cx, NEEDLESS_UPDATE, diff --git a/tests/ui/needless_update.rs b/tests/ui/needless_update.rs index bfa005a19f9..b93ff048a62 100644 --- a/tests/ui/needless_update.rs +++ b/tests/ui/needless_update.rs @@ -6,9 +6,20 @@ struct S { pub b: i32, } +#[non_exhaustive] +struct T { + pub x: i32, + pub y: i32, +} + fn main() { let base = S { a: 0, b: 0 }; S { ..base }; // no error S { a: 1, ..base }; // no error S { a: 1, b: 1, ..base }; + + let base = T { x: 0, y: 0 }; + T { ..base }; // no error + T { x: 1, ..base }; // no error + T { x: 1, y: 1, ..base }; // no error } diff --git a/tests/ui/needless_update.stderr b/tests/ui/needless_update.stderr index 133c834880d..b154b3b306d 100644 --- a/tests/ui/needless_update.stderr +++ b/tests/ui/needless_update.stderr @@ -1,5 +1,5 @@ error: struct update has no effect, all the fields in the struct have already been specified - --> $DIR/needless_update.rs:13:23 + --> $DIR/needless_update.rs:19:23 | LL | S { a: 1, b: 1, ..base }; | ^^^^ From 920c9a4aaefbc4bade7032e32ec099bff1c7b58a Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Fri, 18 Dec 2020 10:55:09 +0530 Subject: [PATCH 1104/1110] add more lints to msrv docs --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 6403ff6dad1..32d7840a451 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), From 1f58c2bb8a638a63edc1f3503c8771937aa19157 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 18 Dec 2020 21:18:15 +0000 Subject: [PATCH 1105/1110] Renamed the good first issue label for rustbot See https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/Rename.20the.20.22good.20first.20issue.22.20label.20for.20bot.20usage/near/220428018 --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index b7b20b40e68..b9549be3a8b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*", - "good first issue" + "good-first-issue" ] [assign] From ced54f28671ddb9ccf9b44131d522f4fdeab7097 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 18 Dec 2020 22:13:22 +0000 Subject: [PATCH 1106/1110] Renamed the good first issue label in CONTRIBUTING.md --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2a3c52dab9..4cfeaa153a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ first read the [Basics docs](doc/basics.md).** All issues on Clippy are mentored, if you want help with a bug just ask @Manishearth, @flip1995, @phansch or @yaahc. -Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues. +Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues. If you want to work on an issue, please leave a comment so that we can assign it to you! There are also some abandoned PRs, marked with [`S-inactive-closed`]. @@ -68,7 +68,7 @@ To figure out how this syntax structure is encoded in the AST, it is recommended Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. -[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. Note that [`E-medium`] issues may require some knowledge of Clippy internals or some debugging to find the actual problem behind the issue. @@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue. lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. -[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue +[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed [`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST [`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle From a24c6f14fa89dafc879ad76a185f43bc50f0bbb9 Mon Sep 17 00:00:00 2001 From: Andrew Houts Date: Fri, 18 Dec 2020 19:15:05 -0600 Subject: [PATCH 1107/1110] remove example --- clippy_lints/src/needless_update.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 1e387b518c3..41cf541ecf5 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -8,6 +8,9 @@ declare_clippy_lint! { /// **What it does:** Checks for needlessly including a base struct on update /// when all fields are changed anyway. /// + /// This lint is not applied to structs marked with + /// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html). + /// /// **Why is this bad?** This will cost resources (because the base has to be /// somewhere), and make the code less readable. /// @@ -21,14 +24,7 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// # - /// # #[non_exhaustive] - /// # struct Options { - /// # a: bool, - /// # b: i32, - /// # } - /// # let default_options = Options { a: false, b: 0 }; - /// # + /// /// // Bad /// Point { /// x: 1, @@ -43,14 +39,6 @@ declare_clippy_lint! { /// y: 1, /// ..zero_point /// }; - /// - /// // this lint is not applied to structs marked with [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html) - /// // Ok - /// Options { - /// a: true, - /// b: 321, - /// ..default_options - /// }; /// ``` pub NEEDLESS_UPDATE, complexity, From dfb4ea588c2af7633b2d01d2dfa3d6ffc7d07ee7 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 19 Dec 2020 08:25:42 +0100 Subject: [PATCH 1108/1110] Fix blessing of new reference files Adding of new reference files wasn't handled correctly. It was trying to read a file that didn't exist yet. Instead of unwrapping, we now treat a missing reference file as empty (`Vec::new`). This makes the following conditional work. We then also have to re-read the reference file after it was being copied. This second read is technically the same as in the old shell script, but wasn't really obvious. The shell script did a `-s` test which reads the file. --- clippy_dev/src/bless.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index 8d5c2e95055..645098e4cfc 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -46,13 +46,16 @@ fn update_reference_file(reference_file_path: PathBuf) { } let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); - let reference_file = fs::read(&reference_file_path).expect("Unable to read reference file"); + let reference_file = fs::read(&reference_file_path).unwrap_or_default(); if test_output_file != reference_file { // If a test run caused an output file to change, update the reference file println!("updating {}", &relative_reference_file_path.display()); fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file"); + // We need to re-read the file now because it was potentially updated from copying + let reference_file = fs::read(&reference_file_path).unwrap_or_default(); + if reference_file.is_empty() { // If we copied over an empty output file, we remove the now empty reference file println!("removing {}", &relative_reference_file_path.display()); From 8ddf4ce87a1a0a50d015e40bf9cebede68f64d0a Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 19 Dec 2020 13:54:38 +0100 Subject: [PATCH 1109/1110] UI Tests: Separate suspicious_else_formatting tests --- tests/ui/formatting.rs | 87 +-------------------- tests/ui/formatting.stderr | 89 ++-------------------- tests/ui/suspicious_else_formatting.rs | 79 +++++++++++++++++++ tests/ui/suspicious_else_formatting.stderr | 77 +++++++++++++++++++ 4 files changed, 164 insertions(+), 168 deletions(-) create mode 100644 tests/ui/suspicious_else_formatting.rs create mode 100644 tests/ui/suspicious_else_formatting.stderr diff --git a/tests/ui/formatting.rs b/tests/ui/formatting.rs index f54b3f2bfe2..0d14807ff1c 100644 --- a/tests/ui/formatting.rs +++ b/tests/ui/formatting.rs @@ -10,91 +10,6 @@ fn foo() -> bool { #[rustfmt::skip] fn main() { - // weird `else` formatting: - if foo() { - } { - } - - if foo() { - } if foo() { - } - - let _ = { // if as the last expression - let _ = 0; - - if foo() { - } if foo() { - } - else { - } - }; - - let _ = { // if in the middle of a block - if foo() { - } if foo() { - } - else { - } - - let _ = 0; - }; - - if foo() { - } else - { - } - - if foo() { - } - else - { - } - - if foo() { - } else - if foo() { // the span of the above error should continue here - } - - if foo() { - } - else - if foo() { // the span of the above error should continue here - } - - // those are ok: - if foo() { - } - { - } - - if foo() { - } else { - } - - if foo() { - } - else { - } - - if foo() { - } - if foo() { - } - - if foo() { - } else if foo() { - } - - if foo() { - } - else if foo() { - } - - if foo() { - } - else if - foo() {} - // weird op_eq formatting: let mut a = 42; a =- 35; @@ -146,7 +61,7 @@ fn main() { // don't lint if the indentation suggests not to let _ = &[ - 1 + 2, 3 + 1 + 2, 3 - 4, 5 ]; // lint if it doesn't diff --git a/tests/ui/formatting.stderr b/tests/ui/formatting.stderr index e2095cc125b..bde434c7e2e 100644 --- a/tests/ui/formatting.stderr +++ b/tests/ui/formatting.stderr @@ -1,80 +1,5 @@ -error: this looks like an `else {..}` but the `else` is missing - --> $DIR/formatting.rs:15:6 - | -LL | } { - | ^ - | - = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` - = note: to remove this lint, add the missing `else` or add a new line before the next block - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:19:6 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:26:10 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:34:10 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this is an `else {..}` but the formatting might hide it - --> $DIR/formatting.rs:43:6 - | -LL | } else - | ______^ -LL | | { - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` - -error: this is an `else {..}` but the formatting might hide it - --> $DIR/formatting.rs:48:6 - | -LL | } - | ______^ -LL | | else -LL | | { - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` - -error: this is an `else if` but the formatting might hide it - --> $DIR/formatting.rs:54:6 - | -LL | } else - | ______^ -LL | | if foo() { // the span of the above error should continue here - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` - -error: this is an `else if` but the formatting might hide it - --> $DIR/formatting.rs:59:6 - | -LL | } - | ______^ -LL | | else -LL | | if foo() { // the span of the above error should continue here - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` - error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` - --> $DIR/formatting.rs:100:6 + --> $DIR/formatting.rs:15:6 | LL | a =- 35; | ^^^^ @@ -83,7 +8,7 @@ LL | a =- 35; = note: to remove this lint, use either `-=` or `= -` error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` - --> $DIR/formatting.rs:101:6 + --> $DIR/formatting.rs:16:6 | LL | a =* &191; | ^^^^ @@ -91,7 +16,7 @@ LL | a =* &191; = note: to remove this lint, use either `*=` or `= *` error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` - --> $DIR/formatting.rs:104:6 + --> $DIR/formatting.rs:19:6 | LL | b =! false; | ^^^^ @@ -99,7 +24,7 @@ LL | b =! false; = note: to remove this lint, use either `!=` or `= !` error: possibly missing a comma here - --> $DIR/formatting.rs:113:19 + --> $DIR/formatting.rs:28:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -108,7 +33,7 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> $DIR/formatting.rs:117:19 + --> $DIR/formatting.rs:32:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -116,12 +41,12 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> $DIR/formatting.rs:154:11 + --> $DIR/formatting.rs:69:11 | LL | -1 | ^ | = note: to remove this lint, add a comma or write the expr in a single line -error: aborting due to 14 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs new file mode 100644 index 00000000000..226010ec6df --- /dev/null +++ b/tests/ui/suspicious_else_formatting.rs @@ -0,0 +1,79 @@ +#![warn(clippy::suspicious_else_formatting)] + +fn foo() -> bool { + true +} + +#[rustfmt::skip] +fn main() { + // weird `else` formatting: + if foo() { + } { + } + + if foo() { + } if foo() { + } + + let _ = { // if as the last expression + let _ = 0; + + if foo() { + } if foo() { + } + else { + } + }; + + let _ = { // if in the middle of a block + if foo() { + } if foo() { + } + else { + } + + let _ = 0; + }; + + if foo() { + } else + { + } + + if foo() { + } + else + { + } + + if foo() { + } else + if foo() { // the span of the above error should continue here + } + + if foo() { + } + else + if foo() { // the span of the above error should continue here + } + + // those are ok: + if foo() { + } + { + } + + if foo() { + } else { + } + + if foo() { + } + else { + } + + if foo() { + } + if foo() { + } +} diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr new file mode 100644 index 00000000000..bbc036d376f --- /dev/null +++ b/tests/ui/suspicious_else_formatting.stderr @@ -0,0 +1,77 @@ +error: this looks like an `else {..}` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:11:6 + | +LL | } { + | ^ + | + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = note: to remove this lint, add the missing `else` or add a new line before the next block + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:15:6 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:22:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:30:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:39:6 + | +LL | } else + | ______^ +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:44:6 + | +LL | } + | ______^ +LL | | else +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:50:6 + | +LL | } else + | ______^ +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:55:6 + | +LL | } + | ______^ +LL | | else +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: aborting due to 8 previous errors + From 2814ee4840c775fcc546c709ffcc63c692eaf548 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 19 Dec 2020 16:12:36 +0000 Subject: [PATCH 1110/1110] Adapted the website search for better matching * Formatting --- util/gh-pages/index.html | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index e11f2eeba3b..428708136cb 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -89,7 +89,7 @@

+ ng-repeat="lint in data | filter:byLevels | filter:byGroups | filter:bySearch | orderBy:'id' track by lint.id">

@@ -215,6 +215,46 @@ return $scope.groups[lint.group]; }; + $scope.bySearch = function (lint, index, array) { + let search_str = $scope.search; + // It can be `null` I haven't missed this value + if (search_str == null || search_str.length == 0) { + return true; + } + search_str = search_str.toLowerCase(); + + // Search by id + let id_search = search_str.trim().replace(/(\-| )/g, "_"); + if (lint.id.includes(id_search)) { + return true; + } + + // Search the description + // The use of `for`-loops instead of `foreach` enables us to return early + let search_lint = (lint, therm) => { + for (const field in lint.docs) { + // Continue if it's not a property + if (!lint.docs.hasOwnProperty(field)) { + continue; + } + + // Return if not found + if (lint.docs[field].toLowerCase().includes(therm)) { + return true; + } + } + return false; + }; + let therms = search_str.split(" "); + for (index = 0; index < therms.length; index++) { + if (!search_lint(lint, therms[index])) { + return false; + } + } + + return true; + } + // Get data $scope.open = {}; $scope.loading = true;

)` instead", ); } } @@ -2895,7 +2895,7 @@ fn lint_filter_map<'tcx>( ) { // lint if caller of `.filter().map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).map(q)` on an `Iterator`"; + let msg = "called `filter(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); } @@ -2904,8 +2904,8 @@ fn lint_filter_map<'tcx>( /// lint use of `filter_map().next()` for `Iterators` fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find_map(p)` instead."; + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { span_lint_and_note( @@ -2931,7 +2931,7 @@ fn lint_find_map<'tcx>( ) { // lint if caller of `.filter().map()` is an Iterator if match_trait_method(cx, &map_args[0], &paths::ITERATOR) { - let msg = "called `find(p).map(q)` on an `Iterator`"; + let msg = "called `find(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.find_map(..)` instead"; span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint); } @@ -2946,7 +2946,7 @@ fn lint_filter_map_map<'tcx>( ) { // lint if caller of `.filter().map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(p).map(q)` on an `Iterator`"; + let msg = "called `filter_map(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); } @@ -2961,7 +2961,7 @@ fn lint_filter_flat_map<'tcx>( ) { // lint if caller of `.filter().flat_map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).flat_map(q)` on an `Iterator`"; + let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ and filtering by returning `iter::empty()`"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); @@ -2977,7 +2977,7 @@ fn lint_filter_map_flat_map<'tcx>( ) { // lint if caller of `.filter_map().flat_map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`"; + let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ and filtering by returning `iter::empty()`"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); @@ -3148,9 +3148,9 @@ fn lint_chars_cmp( "like this", format!("{}{}.{}({})", if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), suggest, - snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)), + snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), applicability, ); @@ -3197,7 +3197,7 @@ fn lint_chars_cmp_with_unwrap<'tcx>( "like this", format!("{}{}.{}('{}')", if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), suggest, c), applicability, @@ -3272,7 +3272,7 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, @@ -3315,7 +3315,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re expr.span, &format!("this call to `{}` does nothing", call_name), "try this", - snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(), + snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, ); } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 95fa28e1c0f..d30b85d6a78 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -53,15 +53,15 @@ pub(super) fn lint<'tcx>( // lint message // comparing the snippet from source to raw text ("None") below is safe // because we already have checked the type. - let arg = if unwrap_snippet == "None" { "None" } else { "a" }; + let arg = if unwrap_snippet == "None" { "None" } else { "" }; let unwrap_snippet_none = unwrap_snippet == "None"; let suggest = if unwrap_snippet_none { - "and_then(f)" + "and_then()" } else { - "map_or(a, f)" + "map_or(, )" }; let msg = &format!( - "called `map(f).unwrap_or({})` on an `Option` value. \ + "called `map().unwrap_or({})` on an `Option` value. \ This can be done more directly by calling `{}` instead", arg, suggest ); diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index d69ae212414..01281ebaf6a 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); @@ -7,7 +7,7 @@ LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next = note: `-D clippy::filter-map-next` implied by `-D warnings` = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` -error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:10:26 | LL | let _: Option = vec![1, 2, 3, 4, 5, 6] diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 84a957a374c..91718dd1175 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,4 +1,4 @@ -error: called `filter(p).map(q)` on an `Iterator` +error: called `filter(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:5:21 | LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); @@ -7,7 +7,7 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead -error: called `filter(p).flat_map(q)` on an `Iterator` +error: called `filter(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:7:21 | LL | let _: Vec<_> = vec![5_i8; 6] @@ -19,7 +19,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: called `filter_map(p).flat_map(q)` on an `Iterator` +error: called `filter_map(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:13:21 | LL | let _: Vec<_> = vec![5_i8; 6] @@ -31,7 +31,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: called `filter_map(p).map(q)` on an `Iterator` +error: called `filter_map(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:19:21 | LL | let _: Vec<_> = vec![5_i8; 6] diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr index f279850fef8..aea3cc62afc 100644 --- a/tests/ui/find_map.stderr +++ b/tests/ui/find_map.stderr @@ -1,4 +1,4 @@ -error: called `find(p).map(q)` on an `Iterator` +error: called `find(..).map(..)` on an `Iterator` --> $DIR/find_map.rs:20:26 | LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); @@ -7,7 +7,7 @@ LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s = note: `-D clippy::find-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.find_map(..)` instead -error: called `find(p).map(q)` on an `Iterator` +error: called `find(..).map(..)` on an `Iterator` --> $DIR/find_map.rs:23:29 | LL | let _: Option = desserts_of_the_week diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index feedc2f288a..486de718bb5 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,4 +1,4 @@ -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:15:28 | LL | let _ = some_vec.iter().skip(42).next(); @@ -6,19 +6,19 @@ LL | let _ = some_vec.iter().skip(42).next(); | = note: `-D clippy::iter-skip-next` implied by `-D warnings` -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:16:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:17:20 | LL | let _ = (1..10).skip(10).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:18:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index b62080a073f..6fa2800e9bd 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,4 +1,4 @@ -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:17:13 | LL | let _ = opt.map(|x| x + 1) @@ -8,12 +8,12 @@ LL | | .unwrap_or(0); | |_____________________^ | = note: `-D clippy::map-unwrap-or` implied by `-D warnings` -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| x + 1); | ^^^^^^ ^^ -- -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:21:13 | LL | let _ = opt.map(|x| { @@ -23,7 +23,7 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { LL | x + 1 @@ -31,7 +31,7 @@ LL | } LL | ); | -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| x + 1) @@ -41,25 +41,25 @@ LL | | 0 LL | | }); | |__________^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or({ LL | 0 LL | }, |x| x + 1); | -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:32:13 | LL | let _ = opt.map(|x| { @@ -69,7 +69,7 @@ LL | | } LL | | ).unwrap_or(None); | |_____________________^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | let _ = opt.and_then(|x| { LL | Some(x + 1) @@ -77,7 +77,7 @@ LL | } LL | ); | -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:36:13 | LL | let _ = opt @@ -86,23 +86,23 @@ LL | | .map(|x| Some(x + 1)) LL | | .unwrap_or(None); | |________________________^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:51:13 | LL | let _ = opt.map(|x| x + 1) @@ -113,7 +113,7 @@ LL | | .unwrap_or_else(|| 0); | = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:55:13 | LL | let _ = opt.map(|x| { @@ -123,7 +123,7 @@ LL | | } LL | | ).unwrap_or_else(|| 0); | |__________________________^ -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:59:13 | LL | let _ = opt.map(|x| x + 1) @@ -133,7 +133,7 @@ LL | | 0 LL | | ); | |_________^ -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:88:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line @@ -141,7 +141,7 @@ LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even t | = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:90:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); @@ -149,7 +149,7 @@ LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); | = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:91:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 2a0a43e83a6..74d8533d4da 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::new-ret-no-self` implied by `-D warnings` -error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); @@ -17,7 +17,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` -error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { diff --git a/tests/ui/option_map_or_none.stderr b/tests/ui/option_map_or_none.stderr index 6f707987dbc..1cba29412b8 100644 --- a/tests/ui/option_map_or_none.stderr +++ b/tests/ui/option_map_or_none.stderr @@ -1,4 +1,4 @@ -error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead --> $DIR/option_map_or_none.rs:10:13 | LL | let _ = opt.map_or(None, |x| Some(x + 1)); @@ -6,7 +6,7 @@ LL | let _ = opt.map_or(None, |x| Some(x + 1)); | = note: `-D clippy::option-map-or-none` implied by `-D warnings` -error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead --> $DIR/option_map_or_none.rs:13:13 | LL | let _ = opt.map_or(None, |x| { diff --git a/tests/ui/skip_while_next.stderr b/tests/ui/skip_while_next.stderr index a6b7bcd63ff..269cc13468b 100644 --- a/tests/ui/skip_while_next.stderr +++ b/tests/ui/skip_while_next.stderr @@ -1,13 +1,13 @@ -error: called `skip_while(p).next()` on an `Iterator` +error: called `skip_while(

6l!wgDv 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 tht&6wB3#E6R1AiS@#JX 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 z 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%0A;?_D0j;DP>l8qFq7!7(_u4e( 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